From 9c4878e8daea2e12724d3e49e57d8120dfd00bcd Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 20 May 2018 15:09:22 -0400 Subject: [PATCH] runelite-client: add screen markers plugin --- .../plugins/screenmarkers/ScreenMarker.java | 45 ++++ .../ScreenMarkerCreationOverlay.java | 77 ++++++ .../ScreenMarkerMouseListener.java | 135 ++++++++++ .../screenmarkers/ScreenMarkerOverlay.java | 67 +++++ .../screenmarkers/ScreenMarkerPlugin.java | 249 ++++++++++++++++++ .../screenmarkers/ScreenMarkerRenderable.java | 78 ++++++ .../screenmarkers/ui/ScreenMarkerPanel.java | 79 ++++++ .../ui/ScreenMarkerPluginPanel.java | 212 +++++++++++++++ .../plugins/screenmarkers/panel_icon.png | Bin 0 -> 155 bytes 9 files changed, 942 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarker.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerCreationOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerMouseListener.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerRenderable.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPluginPanel.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/panel_icon.png 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..2d94c34073 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarker.java @@ -0,0 +1,45 @@ +/* + * 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 java.awt.Rectangle; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ScreenMarker +{ + private Rectangle bounds; + 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..cf22b1d2b9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerCreationOverlay.java @@ -0,0 +1,77 @@ +/* + * 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.Rectangle; +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 +{ + @Inject + private ScreenMarkerPlugin plugin; + + public ScreenMarkerCreationOverlay() + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ALWAYS_ON_TOP); + setPriority(OverlayPriority.HIGH); + } + + @Override + public Dimension render(Graphics2D graphics) + { + ScreenMarker marker = plugin.getCurrentMarker(); + + if (marker == null) + { + return null; + } + + Rectangle bounds = marker.getBounds(); + int thickness = marker.getBorderThickness(); + int offset = thickness / 2; + int width = bounds.width - thickness; + int height = bounds.height - thickness; + + graphics.setStroke(createStripedStroke(thickness)); + graphics.setColor(marker.getColor()); + graphics.drawRect(bounds.x + offset, bounds.y + offset, width, height); + + return bounds.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..0ca8498ac5 --- /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; + + public 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(currentPoint, 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..03435f1565 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerOverlay.java @@ -0,0 +1,67 @@ +/* + * 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 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 +{ + private final ScreenMarker marker; + private final ScreenMarkerRenderable screenMarkerRenderable; + + public ScreenMarkerOverlay(ScreenMarker marker) + { + this.marker = marker; + setPosition(OverlayPosition.DETACHED); + setLayer(OverlayLayer.ALWAYS_ON_TOP); + setPriority(OverlayPriority.HIGH); + setPreferredLocation(marker.getBounds().getLocation()); + + screenMarkerRenderable = new ScreenMarkerRenderable(); + screenMarkerRenderable.setBorderThickness(marker.getBorderThickness()); + screenMarkerRenderable.setColor(marker.getColor()); + screenMarkerRenderable.setFill(marker.getFill()); + screenMarkerRenderable.setStroke(new BasicStroke(marker.getBorderThickness())); + screenMarkerRenderable.setPreferredSize(marker.getBounds().getSize()); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!marker.isVisible()) + { + return null; + } + + 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..59ff80fcc4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerPlugin.java @@ -0,0 +1,249 @@ +/* + * 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.Rectangle; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +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 List screenMarkers = new ArrayList<>(); + + @Getter(AccessLevel.PACKAGE) + private ScreenMarker currentMarker; + + @Getter + private boolean creatingScreenMarker = false; + + @Override + public Collection getOverlays() + { + List overlays = new ArrayList<>(); + overlays.add(overlay); + + for (ScreenMarker marker : screenMarkers) + { + ScreenMarkerOverlay screenMarkerOverlay = new ScreenMarkerOverlay(marker); + overlays.add(screenMarkerOverlay); + } + + return overlays; + } + + @Override + protected void startUp() throws Exception + { + screenMarkers = loadConfig(null); + 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)) + { + screenMarkers = loadConfig(event.getNewValue()); + } + } + + public void setMouseListenerEnabled(boolean enabled) + { + if (enabled) + { + mouseManager.registerMouseListener(mouseListener); + } + else + { + mouseManager.unregisterMouseListener(mouseListener); + } + } + + public void startCreation(Point location) + { + currentMarker = new ScreenMarker( + new Rectangle(location, DEFAULT_SIZE), + DEFAULT_MARKER_NAME, + pluginPanel.getSelectedBorderThickness(), + pluginPanel.getSelectedColor(), + pluginPanel.getSelectedFillColor(), + true + ); + creatingScreenMarker = true; + } + + public void finishCreation(boolean aborted) + { + if (!aborted) + { + setMouseListenerEnabled(false); + pluginPanel.setCreationEnabled(false); + screenMarkers.add(currentMarker); + pluginPanel.rebuild(); + updateConfig(); + overlayRenderer.rebuildOverlays(); + } + + creatingScreenMarker = false; + currentMarker = null; + } + + public void deleteMarker(ScreenMarker marker) + { + screenMarkers.remove(marker); + pluginPanel.rebuild(); + updateConfig(); + overlayRenderer.rebuildOverlays(); + } + + public void resizeMarker(Point point, int dx, int dy) + { + //TODO: Allow resizing below base point + Dimension currentSize = currentMarker.getBounds().getSize(); + currentMarker.getBounds().setSize(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(); + String json = gson.toJson(screenMarkers); + configManager.setConfiguration(CONFIG_GROUP, CONFIG_KEY, json); + } + + private List loadConfig(String json) + { + if (json == null) + { + json = configManager.getConfiguration(CONFIG_GROUP, CONFIG_KEY); + } + + if (Strings.isNullOrEmpty(json)) + { + return Collections.emptyList(); + } + + final Gson gson = new Gson(); + return gson.fromJson(json, new TypeToken>() + { + }.getType()); + } +} 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..197d8f70b6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java @@ -0,0 +1,79 @@ +/* + * 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.ScreenMarker; +import net.runelite.client.plugins.screenmarkers.ScreenMarkerPlugin; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; + +public 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 ScreenMarker marker; + private JToggleButton visibleToggle; + + + public ScreenMarkerPanel(ScreenMarkerPlugin plugin, ScreenMarker 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.isVisible(); + visibleToggle = new JToggleButton(selected ? SHOW_TEXT : HIDE_TEXT, selected); + visibleToggle.setFocusable(false); + visibleToggle.addActionListener(l -> + { + boolean visible = !visibleToggle.isSelected(); + marker.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..142e2b39c3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPluginPanel.java @@ -0,0 +1,212 @@ +/* + * 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.ScreenMarker; +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 JPanel markerView; + 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(); + markerView = new JPanel(new GridLayout(0, 1, 0, 3)); + + for (ScreenMarker 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/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 0000000000000000000000000000000000000000..0204813fcfa13f625d7160e8aafc5c6c3e8144a1 GIT binary patch literal 155 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucK?6@0#}EtusmC|+G8k|iF!*Obo&D51)m2Z9 le*V_)Z};=bWE5gUvDvnd0=Ew?C;{qa@O1TaS?83{1OWXYGQ9u* literal 0 HcmV?d00001