From b9f1b6e1ef9373e445854f39c2de4018125ea101 Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Thu, 24 May 2018 09:01:38 +0100 Subject: [PATCH] Screenmarkers plugin redesign (#3139) - Update the interface to match with the Obsidian redesign - Remove displaying of screen marker names (they are not editable so not important) - Add support for changing screen markers after creation - Add support for toggling fill --- .../ScreenMarkerMouseListener.java | 3 +- .../screenmarkers/ScreenMarkerOverlay.java | 11 +- .../screenmarkers/ScreenMarkerPlugin.java | 22 +- .../ui/ScreenMarkerCreationPanel.java | 158 +++++++ .../screenmarkers/ui/ScreenMarkerPanel.java | 397 ++++++++++++++++-- .../ui/ScreenMarkerPluginPanel.java | 255 ++++++----- .../plugins/screenmarkers/add_hover_icon.png | Bin 0 -> 15845 bytes .../client/plugins/screenmarkers/add_icon.png | Bin 0 -> 15403 bytes .../screenmarkers/border_color_icon.png | Bin 0 -> 15078 bytes .../screenmarkers/cancel_hover_icon.png | Bin 0 -> 16152 bytes .../plugins/screenmarkers/cancel_icon.png | Bin 0 -> 15498 bytes .../screenmarkers/confirm_hover_icon.png | Bin 0 -> 15951 bytes .../plugins/screenmarkers/confirm_icon.png | Bin 0 -> 15513 bytes .../screenmarkers/confirm_locked_icon.png | Bin 0 -> 15950 bytes .../plugins/screenmarkers/delete_icon.png | Bin 0 -> 14651 bytes .../plugins/screenmarkers/fill_color_icon.png | Bin 0 -> 15128 bytes .../plugins/screenmarkers/invisible_icon.png | Bin 0 -> 15243 bytes .../screenmarkers/no_border_color_icon.png | Bin 0 -> 15080 bytes .../screenmarkers/no_fill_color_icon.png | Bin 0 -> 15125 bytes .../plugins/screenmarkers/opacity_icon.png | Bin 0 -> 213 bytes .../plugins/screenmarkers/visible_icon.png | Bin 0 -> 255 bytes 21 files changed, 674 insertions(+), 172 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerCreationPanel.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/add_hover_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/add_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/border_color_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/cancel_hover_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/cancel_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/confirm_hover_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/confirm_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/confirm_locked_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/delete_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/fill_color_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/invisible_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/no_border_color_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/no_fill_color_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/opacity_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/visible_icon.png 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 index 611c4390a9..b62dc512d5 100644 --- 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 @@ -88,7 +88,8 @@ public class ScreenMarkerMouseListener extends MouseListener if (SwingUtilities.isLeftMouseButton(event) && plugin.isCreatingScreenMarker()) { - plugin.finishCreation(false); + /* Set the creation panel as "ready" (because the marker area as been drawn) */ + plugin.completeSelection(); } event.consume(); 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 index 10c65d8da0..b0321fd29a 100644 --- 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 @@ -43,15 +43,10 @@ public class ScreenMarkerOverlay extends Overlay ScreenMarkerOverlay(ScreenMarker marker) { this.marker = marker; + this.screenMarkerRenderable = new ScreenMarkerRenderable(); 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 @@ -68,6 +63,10 @@ public class ScreenMarkerOverlay extends Overlay return null; } + screenMarkerRenderable.setBorderThickness(marker.getBorderThickness()); + screenMarkerRenderable.setColor(marker.getColor()); + screenMarkerRenderable.setFill(marker.getFill()); + screenMarkerRenderable.setStroke(new BasicStroke(marker.getBorderThickness())); 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 index 2f5632adee..cc1b2803f3 100644 --- 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 @@ -1,6 +1,7 @@ /* * Copyright (c) 2018, Kamiel, * Copyright (c) 2018, Adam + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,7 +27,6 @@ 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; @@ -67,8 +67,8 @@ public class ScreenMarkerPlugin extends Plugin private static final String DEFAULT_MARKER_NAME = "Marker"; private static final Dimension DEFAULT_SIZE = new Dimension(2, 2); - @Inject - private EventBus eventBus; + @Getter + private final List screenMarkers = new ArrayList<>(); @Inject private ConfigManager configManager; @@ -89,9 +89,6 @@ public class ScreenMarkerPlugin extends Plugin private ScreenMarkerPluginPanel pluginPanel; private NavigationButton navigationButton; - @Getter - private final List screenMarkers = new ArrayList<>(); - @Getter(AccessLevel.PACKAGE) private ScreenMarker currentMarker; @@ -125,6 +122,7 @@ public class ScreenMarkerPlugin extends Plugin navigationButton = NavigationButton.builder() .tooltip(PLUGIN_NAME) .icon(icon) + .priority(5) .panel(pluginPanel) .build(); @@ -189,9 +187,6 @@ public class ScreenMarkerPlugin extends Plugin { if (!aborted) { - setMouseListenerEnabled(false); - pluginPanel.setCreationEnabled(false); - final ScreenMarkerOverlay screenMarkerOverlay = new ScreenMarkerOverlay(currentMarker); screenMarkerOverlay.setPreferredLocation(overlay.getBounds().getLocation()); screenMarkerOverlay.setPreferredSize(overlay.getBounds().getSize()); @@ -205,6 +200,15 @@ public class ScreenMarkerPlugin extends Plugin creatingScreenMarker = false; currentMarker = null; + setMouseListenerEnabled(false); + + pluginPanel.setCreation(false); + } + + /* The marker area has been drawn, inform the user and unlock the confirm button */ + public void completeSelection() + { + pluginPanel.getCreationPanel().unlockConfirm(); } public void deleteMarker(final ScreenMarkerOverlay marker) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerCreationPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerCreationPanel.java new file mode 100644 index 0000000000..6385fa3163 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerCreationPanel.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2018, Psikoi + * 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.BorderLayout; +import java.awt.Color; +import java.awt.GridLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.IOException; +import javax.imageio.ImageIO; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; +import net.runelite.client.plugins.screenmarkers.ScreenMarkerPlugin; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; + +public class ScreenMarkerCreationPanel extends JPanel +{ + private static final ImageIcon CONFIRM_ICON; + private static final ImageIcon CONFIRM_HOVER_ICON; + private static final ImageIcon CONFIRM_LOCKED_ICON; + private static final ImageIcon CANCEL_ICON; + private static final ImageIcon CANCEL_HOVER_ICON; + + private final JShadowedLabel instructionsLabel = new JShadowedLabel(); + private final JLabel confirmLabel = new JLabel(); + private boolean lockedConfirm = true; + + static + { + try + { + synchronized (ImageIO.class) + { + CONFIRM_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("confirm_icon.png"))); + CONFIRM_HOVER_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("confirm_hover_icon.png"))); + CONFIRM_LOCKED_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("confirm_locked_icon.png"))); + CANCEL_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("cancel_icon.png"))); + CANCEL_HOVER_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("cancel_hover_icon.png"))); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + ScreenMarkerCreationPanel(ScreenMarkerPlugin plugin) + { + setBackground(ColorScheme.DARKER_GRAY_COLOR); + setBorder(new EmptyBorder(8, 8, 8, 8)); + setLayout(new BorderLayout()); + + instructionsLabel.setFont(FontManager.getRunescapeSmallFont()); + instructionsLabel.setForeground(Color.WHITE); + + JPanel actionsContainer = new JPanel(new GridLayout(1, 2, 8, 0)); + actionsContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + confirmLabel.setIcon(CONFIRM_LOCKED_ICON); + confirmLabel.setToolTipText("Confirm and save"); + confirmLabel.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + /* If the confirm button is not locked */ + if (!lockedConfirm) + { + plugin.finishCreation(false); + } + } + + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + confirmLabel.setIcon(lockedConfirm ? CONFIRM_LOCKED_ICON : CONFIRM_HOVER_ICON); + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + confirmLabel.setIcon(lockedConfirm ? CONFIRM_LOCKED_ICON : CONFIRM_ICON); + } + }); + + JLabel cancelLabel = new JLabel(CANCEL_ICON); + cancelLabel.setToolTipText("Cancel"); + cancelLabel.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + plugin.finishCreation(true); + } + + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + cancelLabel.setIcon(CANCEL_HOVER_ICON); + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + cancelLabel.setIcon(CANCEL_ICON); + } + }); + + actionsContainer.add(confirmLabel); + actionsContainer.add(cancelLabel); + + add(instructionsLabel, BorderLayout.CENTER); + add(actionsContainer, BorderLayout.EAST); + } + + /* Unlocks the confirm button */ + public void unlockConfirm() + { + this.confirmLabel.setIcon(CONFIRM_ICON); + this.lockedConfirm = false; + instructionsLabel.setText("Confirm or cancel to finish."); + } + + /* Locks the confirm button */ + public void lockConfirm() + { + this.confirmLabel.setIcon(CONFIRM_LOCKED_ICON); + this.lockedConfirm = true; + instructionsLabel.setText("Drag in-game to draw"); + } +} 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 index 13b62aceae..5a297299a3 100644 --- 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 @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Kamiel, + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,55 +25,395 @@ */ package net.runelite.client.plugins.screenmarkers.ui; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; import java.awt.FlowLayout; -import java.awt.GridLayout; -import javax.swing.JButton; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.io.IOException; +import javax.imageio.ImageIO; +import javax.swing.ImageIcon; +import javax.swing.JColorChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JPanel; -import javax.swing.JToggleButton; +import javax.swing.JSpinner; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.border.EmptyBorder; +import javax.swing.border.MatteBorder; import net.runelite.client.plugins.screenmarkers.ScreenMarkerOverlay; import net.runelite.client.plugins.screenmarkers.ScreenMarkerPlugin; -import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.util.SwingUtil; 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 static final int DEFAULT_FILL_OPACITY = 75; + + private static final ImageIcon BORDER_COLOR_ICON; + private static final ImageIcon BORDER_COLOR_HOVER_ICON; + private static final ImageIcon NO_BORDER_COLOR_ICON; + private static final ImageIcon NO_BORDER_COLOR_HOVER_ICON; + + private static final ImageIcon FILL_COLOR_ICON; + private static final ImageIcon FILL_COLOR_HOVER_ICON; + private static final ImageIcon NO_FILL_COLOR_ICON; + private static final ImageIcon NO_FILL_COLOR_HOVER_ICON; + + private static final ImageIcon FULL_OPACITY_ICON; + private static final ImageIcon OPACITY_HOVER_ICON; + private static final ImageIcon NO_OPACITY_ICON; + + private static final ImageIcon VISIBLE_ICON; + private static final ImageIcon VISIBLE_HOVER_ICON; + private static final ImageIcon INVISIBLE_ICON; + private static final ImageIcon INVISIBLE_HOVER_ICON; + + private static final ImageIcon DELETE_ICON; + private static final ImageIcon DELETE_HOVER_ICON; private final ScreenMarkerPlugin plugin; private final ScreenMarkerOverlay marker; - private JToggleButton visibleToggle; + + private final JLabel borderColorIndicator = new JLabel(); + private final JLabel fillColorIndicator = new JLabel(); + private final JLabel opacityIndicator = new JLabel(); + private final JLabel visibilityLabel = new JLabel(); + private final JLabel deleteLabel = new JLabel(); + + private final SpinnerModel spinnerModel = new SpinnerNumberModel(5, 0, Integer.MAX_VALUE, 1); + private final JSpinner thicknessSpinner = new JSpinner(spinnerModel); + + private boolean visible; + + static + { + try + { + synchronized (ImageIO.class) + { + BufferedImage borderImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("border_color_icon.png")); + BORDER_COLOR_ICON = new ImageIcon(borderImg); + BORDER_COLOR_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(borderImg, -100)); + + BufferedImage noBorderImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("no_border_color_icon.png")); + NO_BORDER_COLOR_ICON = new ImageIcon(noBorderImg); + NO_BORDER_COLOR_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(noBorderImg, -100)); + + BufferedImage fillImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("fill_color_icon.png")); + FILL_COLOR_ICON = new ImageIcon(fillImg); + FILL_COLOR_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(fillImg, -100)); + + BufferedImage noFillImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("no_fill_color_icon.png")); + NO_FILL_COLOR_ICON = new ImageIcon(noFillImg); + NO_FILL_COLOR_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(noFillImg, -100)); + + BufferedImage opacityImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("opacity_icon.png")); + FULL_OPACITY_ICON = new ImageIcon(opacityImg); + OPACITY_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(opacityImg, -100)); + NO_OPACITY_ICON = new ImageIcon(SwingUtil.grayscaleOffset(opacityImg, -150)); + + BufferedImage visibleImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("visible_icon.png")); + VISIBLE_ICON = new ImageIcon(visibleImg); + VISIBLE_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(visibleImg, -100)); + + BufferedImage invisibleImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("invisible_icon.png")); + INVISIBLE_ICON = new ImageIcon(invisibleImg); + INVISIBLE_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(invisibleImg, -100)); + + BufferedImage deleteImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("delete_icon.png")); + DELETE_ICON = new ImageIcon(deleteImg); + DELETE_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(deleteImg, -100)); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } ScreenMarkerPanel(ScreenMarkerPlugin plugin, ScreenMarkerOverlay marker) { this.plugin = plugin; this.marker = marker; - construct(); - } + this.visible = marker.getMarker().isVisible(); - private void construct() - { - setLayout(new GridLayout(0, 1, 0, 3)); - JPanel container = new JPanel(new FlowLayout()); + setLayout(new BorderLayout()); + setBackground(ColorScheme.DARKER_GRAY_COLOR); - JButton deleteButton = new JButton(DELETE_TEXT); - deleteButton.addActionListener(l -> plugin.deleteMarker(marker)); + JPanel bottomContainer = new JPanel(new BorderLayout()); + bottomContainer.setBorder(new EmptyBorder(8, 0, 8, 0)); + bottomContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); - boolean selected = !marker.getMarker().isVisible(); - visibleToggle = new JToggleButton(selected ? SHOW_TEXT : HIDE_TEXT, selected); - visibleToggle.setFocusable(false); - visibleToggle.addActionListener(l -> + JPanel leftActions = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0)); + leftActions.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + borderColorIndicator.setToolTipText("Edit border color"); + borderColorIndicator.addMouseListener(new MouseAdapter() { - boolean visible = !visibleToggle.isSelected(); - marker.getMarker().setVisible(visible); - visibleToggle.setText(visible ? HIDE_TEXT : SHOW_TEXT); - plugin.updateConfig(); + @Override + public void mousePressed(MouseEvent mouseEvent) + { + openBorderColorPicker(); + } + + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + borderColorIndicator.setIcon(marker.getMarker().getBorderThickness() == 0 ? NO_BORDER_COLOR_HOVER_ICON : BORDER_COLOR_HOVER_ICON); + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + borderColorIndicator.setIcon(marker.getMarker().getBorderThickness() == 0 ? NO_BORDER_COLOR_ICON : BORDER_COLOR_ICON); + } }); - container.add(new JShadowedLabel(marker.getName())); - container.add(visibleToggle); - container.add(deleteButton); + fillColorIndicator.setToolTipText("Edit fill color"); + fillColorIndicator.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + openFillColorPicker(); + } - add(container); + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + fillColorIndicator.setIcon(marker.getMarker().getFill().getAlpha() == 0 ? NO_FILL_COLOR_HOVER_ICON : FILL_COLOR_HOVER_ICON); + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + fillColorIndicator.setIcon(marker.getMarker().getFill().getAlpha() == 0 ? NO_FILL_COLOR_ICON : FILL_COLOR_ICON); + } + }); + + thicknessSpinner.setValue(marker.getMarker().getBorderThickness()); + thicknessSpinner.setPreferredSize(new Dimension(50, 20)); + thicknessSpinner.addChangeListener(ce -> updateThickness(true)); + + opacityIndicator.setToolTipText("Toggle background transparency"); + opacityIndicator.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + final Color fill = marker.getMarker().getFill(); + + if (fill.getAlpha() == 0) + { + marker.getMarker().setFill(new Color(fill.getRed(), fill.getGreen(), fill.getBlue(), DEFAULT_FILL_OPACITY)); + } + else + { + marker.getMarker().setFill(new Color(fill.getRed(), fill.getGreen(), fill.getBlue(), 0)); + } + + updateFill(); + plugin.updateConfig(); + } + + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + opacityIndicator.setIcon(OPACITY_HOVER_ICON); + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + opacityIndicator.setIcon(marker.getMarker().getFill().getAlpha() == 0 ? NO_OPACITY_ICON : FULL_OPACITY_ICON); + } + }); + + leftActions.add(borderColorIndicator); + leftActions.add(fillColorIndicator); + leftActions.add(opacityIndicator); + leftActions.add(thicknessSpinner); + + JPanel rightActions = new JPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0)); + rightActions.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + visibilityLabel.setToolTipText(visible ? "Hide screen marker" : "Show screen marker"); + visibilityLabel.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + visible = !visible; + marker.getMarker().setVisible(visible); + plugin.updateConfig(); + updateVisibility(); + } + + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + visibilityLabel.setIcon(visible ? VISIBLE_HOVER_ICON : INVISIBLE_HOVER_ICON); + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + updateVisibility(); + } + }); + + deleteLabel.setIcon(DELETE_ICON); + deleteLabel.setToolTipText("Delete screen marker"); + deleteLabel.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + int confirm = JOptionPane.showConfirmDialog(null, + "Are you sure you want to permanenely delete this screen marker?", + "Warning", JOptionPane.OK_CANCEL_OPTION); + + if (confirm == 0) + { + plugin.deleteMarker(marker); + } + } + + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + deleteLabel.setIcon(DELETE_HOVER_ICON); + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + deleteLabel.setIcon(DELETE_ICON); + } + }); + + rightActions.add(visibilityLabel); + rightActions.add(deleteLabel); + + bottomContainer.add(leftActions, BorderLayout.WEST); + bottomContainer.add(rightActions, BorderLayout.EAST); + + add(bottomContainer, BorderLayout.CENTER); + + updateVisibility(); + updateFill(); + updateBorder(); + updateBorder(); + + } + + /* Updates the thickness without saving on config */ + private void updateThickness(boolean save) + { + marker.getMarker().setBorderThickness((Integer) thicknessSpinner.getValue()); + updateBorder(); + if (save) + { + plugin.updateConfig(); + } + } + + private void updateVisibility() + { + visibilityLabel.setIcon(visible ? VISIBLE_ICON : INVISIBLE_ICON); + } + + private void updateFill() + { + final boolean isFullyTransparent = marker.getMarker().getFill().getAlpha() == 0; + + if (isFullyTransparent) + { + fillColorIndicator.setBorder(null); + } + else + { + Color color = marker.getMarker().getFill(); + Color fullColor = new Color(color.getRed(), color.getGreen(), color.getBlue()); + fillColorIndicator.setBorder(new MatteBorder(0, 0, 3, 0, fullColor)); + } + + fillColorIndicator.setIcon(isFullyTransparent ? NO_FILL_COLOR_ICON : FILL_COLOR_ICON); + opacityIndicator.setIcon(isFullyTransparent ? NO_OPACITY_ICON : FULL_OPACITY_ICON); + } + + private void updateBorder() + { + if (marker.getMarker().getBorderThickness() == 0) + { + borderColorIndicator.setBorder(null); + } + else + { + Color color = marker.getMarker().getColor(); + borderColorIndicator.setBorder(new MatteBorder(0, 0, 3, 0, color)); + } + + borderColorIndicator.setIcon(marker.getMarker().getBorderThickness() == 0 ? NO_BORDER_COLOR_ICON : BORDER_COLOR_ICON); + } + + private void openFillColorPicker() + { + final JFrame parent = new JFrame(); + JColorChooser jColorChooser = new JColorChooser(marker.getMarker().getFill()); + + jColorChooser.getSelectionModel().addChangeListener(e1 -> + { + Color chosen = jColorChooser.getColor(); + marker.getMarker().setFill(new Color(chosen.getRed(), chosen.getGreen(), chosen.getBlue(), DEFAULT_FILL_OPACITY)); + updateFill(); + }); + + parent.addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent e) + { + plugin.updateConfig(); + } + }); + + parent.add(jColorChooser); + parent.pack(); + parent.setLocationRelativeTo(null); + parent.setVisible(true); + } + + private void openBorderColorPicker() + { + final JFrame parent = new JFrame(); + JColorChooser jColorChooser = new JColorChooser(marker.getMarker().getColor()); + + jColorChooser.getSelectionModel().addChangeListener(e1 -> + { + marker.getMarker().setColor(jColorChooser.getColor()); + updateBorder(); + }); + + parent.addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent e) + { + plugin.updateConfig(); + } + }); + + parent.add(jColorChooser); + parent.pack(); + parent.setLocationRelativeTo(null); + parent.setVisible(true); } } 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 index eb844e3f4b..9dca4e8d11 100644 --- 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 @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Kamiel, + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,84 +29,147 @@ 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 java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.IOException; +import javax.imageio.ImageIO; +import javax.swing.Box; +import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; +import javax.swing.border.EmptyBorder; import lombok.Getter; import net.runelite.client.plugins.screenmarkers.ScreenMarkerOverlay; import net.runelite.client.plugins.screenmarkers.ScreenMarkerPlugin; +import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; -import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; +import net.runelite.client.ui.components.PluginErrorPanel; @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(); + private static final ImageIcon ADD_ICON; + private static final ImageIcon ADD_HOVER_ICON; + + private static final Color DEFAULT_BORDER_COLOR = Color.GREEN; + private static final Color DEFAULT_FILL_COLOR = new Color(0, 255, 0, 0); + + private static final int DEFAULT_BORDER_THICKNESS = 3; + + private final JLabel addMarker = new JLabel(ADD_ICON); + private final JLabel title = new JLabel(); + private final PluginErrorPanel noMarkersPanel = new PluginErrorPanel(); @Inject private ScreenMarkerPlugin plugin; - private JButton markerButton; + @Getter + private Color selectedColor = DEFAULT_BORDER_COLOR; @Getter - private Color selectedColor = DEFAULT_COLOR; - - @Getter - private Color selectedFillColor = new Color(DEFAULT_COLOR.getRed(), DEFAULT_COLOR.getGreen(), DEFAULT_COLOR.getBlue(), DEFAULT_FILL_OPACITY); + private Color selectedFillColor = DEFAULT_FILL_COLOR; @Getter private int selectedBorderThickness = DEFAULT_BORDER_THICKNESS; - private boolean creationEnabled = false; + @Getter + private ScreenMarkerCreationPanel creationPanel; + + static + { + try + { + synchronized (ImageIO.class) + { + ADD_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("add_icon.png"))); + ADD_HOVER_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("add_hover_icon.png"))); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } public void init() { setLayout(new BorderLayout()); - JPanel northPanel = new JPanel(new GridLayout(0, 2, 0, 3)); + setBorder(new EmptyBorder(10, 10, 10, 10)); - markerButton = new JButton(NEW_TEXT); - markerButton.setFocusable(false); - markerButton.addActionListener(l -> startOrCancelCreation()); + JPanel northPanel = new JPanel(new BorderLayout()); + northPanel.setBorder(new EmptyBorder(1, 0, 10, 0)); - 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()); + title.setText("Screen Markers"); + title.setForeground(Color.WHITE); - northPanel.add(markerButton); + northPanel.add(title, BorderLayout.WEST); + northPanel.add(addMarker, BorderLayout.EAST); - JPanel centerPanel = new JPanel(); - JPanel markerView = new JPanel(new GridLayout(0, 1, 0, 3)); + JPanel centerPanel = new JPanel(new BorderLayout()); + centerPanel.setBackground(ColorScheme.DARK_GRAY_COLOR); + + JPanel markerView = new JPanel(new GridBagLayout()); + markerView.setBackground(ColorScheme.DARK_GRAY_COLOR); + + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.weightx = 1; + constraints.gridx = 0; + constraints.gridy = 0; for (final ScreenMarkerOverlay marker : plugin.getScreenMarkers()) { - markerView.add(new ScreenMarkerPanel(plugin, marker)); + markerView.add(new ScreenMarkerPanel(plugin, marker), constraints); + constraints.gridy++; + + markerView.add(Box.createRigidArea(new Dimension(0, 10)), constraints); + constraints.gridy++; } - centerPanel.add(markerView, BorderLayout.NORTH); + noMarkersPanel.setContent("Screen Markers", "Highlight a region on your screen."); + noMarkersPanel.setVisible(false); + + if (plugin.getScreenMarkers().isEmpty()) + { + noMarkersPanel.setVisible(true); + title.setVisible(false); + } + + markerView.add(noMarkersPanel, constraints); + constraints.gridy++; + + creationPanel = new ScreenMarkerCreationPanel(plugin); + creationPanel.setVisible(false); + + markerView.add(creationPanel, constraints); + constraints.gridy++; + + addMarker.setToolTipText("Add new screen marker"); + addMarker.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + setCreation(true); + } + + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + addMarker.setIcon(ADD_HOVER_ICON); + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + addMarker.setIcon(ADD_ICON); + } + }); + + centerPanel.add(markerView, BorderLayout.CENTER); add(northPanel, BorderLayout.NORTH); add(centerPanel, BorderLayout.CENTER); @@ -119,93 +183,28 @@ public class ScreenMarkerPluginPanel extends PluginPanel init(); } - private void startOrCancelCreation() + /* Enables/Disables new marker creation mode */ + public void setCreation(boolean on) { - 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 -> + if (on) { - 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 -> + noMarkersPanel.setVisible(false); + title.setVisible(true); + } + else { - final JFrame parent = new JFrame(COLOR_PICKER_TITLE); - JColorChooser jColorChooser = new JColorChooser(selectedFillColor); - jColorChooser.getSelectionModel().addChangeListener(e1 -> colorPicker.setBackground(jColorChooser.getColor())); + boolean empty = plugin.getScreenMarkers().isEmpty(); + noMarkersPanel.setVisible(empty); + title.setVisible(!empty); + } - 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); - } - }); + creationPanel.setVisible(on); + addMarker.setVisible(!on); - parent.add(jColorChooser); - parent.pack(); - parent.setLocationRelativeTo(null); - parent.setVisible(true); - }); - - return colorPicker; + if (on) + { + creationPanel.lockConfirm(); + plugin.setMouseListenerEnabled(true); + } } } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/add_hover_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/add_hover_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9eb7fe3baa3af0e9cb5ddf933f842503ab45f5da GIT binary patch literal 15845 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 Rx}Zrl&0Y}RIcwSD{{RaX-_8I4 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/border_color_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/border_color_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4cfc549d35f911e27fa209d107b27706f602ade3 GIT binary patch literal 15078 zcmeI3Pl()99LHZpO1tY8*%CMgo-93xNKsIGSn<$ytm@pZ(6vvIoHU!Qit8 zd~*3Q0CK;Xi%b4ez31bsxsCAB3|&MG)I^r= zVLy4OpHsJe+pF6nQugGLiSe3LD!tr8ueaG8n%eWH*D-<)(xIN-;)a971?1Ulu8yYH z@vTe^yL;cP^%rMHq(B*Y?Wk>za0`^kEi8p-h4T1bwUUdVZ@Y`O-6%#?Hs3|al}ce% zafi>EmSMNNr}HA|(qHd>G*pH@D&l&~3Q`&u- zRFw1Lu;G|jSZUg<<@wOk(QLVh2NI@fXcA1Rs+>iFl95#bWmAff)up76H4Lc8=~~s0 zk|D%OCGI{Dx@_xfLAT)0(Z*ufIu75ztZFi+W|eA2NFzBT$TG?b)s&tRjB2(f>)EQ3 zmQgoREZ9Jz7hMzYd$19k6Woh|lgU*D!nCRjD3{6zDVRb+RaXtcfI3VWQcbC)U^m5( zFaxP(Ob=J@+)Xjf>M;SS|M*F!=775Hug_O`U%jurTXeXKz}*xQPF z&>qOhi#ta9m(}y^xS4~?xjO_={FwXb9IZOCXzdk+3TKTD;@LI71zmJ}1y{&!IzNiK z(~S(D7lZ$ThWQ^Xga_B}Z5ejgRrj_GJ9eo5ZyEYktkDu=^ zW0`(Y>0~2@V_v~OXtceb#t(a_m`SIT>AXk|lOFSC8#R{7CfbN}0 zF(z~zqos;HmW4eSjpBzs{Jbgl9ykZuv$u=g&y)Tg-Os2*ES7`-LN^&MIv+x1xCkJ0 zli{NCAykHo075qzE;=7VWw;0+bd%wt^C47*ivU7587?{>LS?uJAas-AqVpkChKm3~ zHyJKEA3|lg2q1Km;iB^)RECQHLN^&MIv+x1xCkJ0li{NCAykHo075qzE;=7VWw;0+ zbd%wt^C47*ivU7587?{>LS?uJAas-AqVpkChKm3~HyJKEA3|lg2q1Km;iB^)RECQH zLN^&MIv+x1xCkJ0li{NCAykHo075qzE;=7VWw;0+bd!o}tp5rpvhaJGE&PJ!{>hhK z#xHkr`piNFfXycXcw-BIyZ_+dI{+-~1A87>0zkV2z@zpL=brcg&&6+A9bat!_{Def zfn)OzzqP%6=(%4Gja`~wEq{LN*-yu3E^obZA^)o|dGX^~^6t%vZ@m+*pLlz7`lt7< z?>{Jg$UpSa)N5ex_}a0p`h}bCpZosSyN%mN9ys{Nm*A#Uyp#1yS8hE2 k%?rZ0z!~dGynhq literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/cancel_hover_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/cancel_hover_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d1ddc4ef676e443e93d22a42ec697ece35dcffc7 GIT binary patch literal 16152 zcmeI3e{3699l$T8VQC;CT7i~yFxLZ?Ha*|@?DLPC<0iGc#?#yyC!=W*RnK?toNMem z_wG_Vp_@XpO(3SSL175SP}f1}Sc?#hA6iEnt*Sa97-cQ(royUD(6EYus475g3-A2f zFV0h?6`@Jbl70TZ@B4n=d!P4x-|yWY_r=7<4IS;>?G#0I#0H~DcmZSRG@w+_;$ z;blo~aEnP%?^|krTc~e7ww$6`d}<_;NQ~;1ZjS0XM=TO?a{qr09@ ze6at;FFkQ7(YttOuJF~Ko}a$H{p6GM`mU$Xu6*J5Pwu}Xc~Kez5+_lf1~^nFhaTxj{8 zpq}dwQQzJ62GhUl)LR`rtIk~=*i9)fUfa$7nev>vx0M~Y4z=uv3CZ+xL-Y^64r0%K zYw;tYj{%`uHr947{n{s=I&i@C@Z3$uQ@=c~3C7_aTRwN?P0x|bzq;_pFLzCiOs?5F z5E#Dv>gtES{p#gQCkr3x)WylnY$WWOI-=)U7S#L}?6Ijuc3%TA%Z8&mMC+RJqT&nUAyaTm+1& zgOdgrn%p=nO^!)H*||2{9x4dXK^9n|qma#Lrcmf}7X1qFzun9@9mOuzSf6vi9?&ro zPdFmF0UW%`M@y{F@9+m*Zr<Kx^i0B_|a>QD91jnzK}0 zbJ%{xls<0tIi2=E)lYd{*<5uX%`CHn6q$mUW85y5S->bB$D3udO)X7p{Vq6@^2(YH zXbw;00Fwl!K5j^0{Vt$cw>6uDELHvF#*Ivok1R1D1G3P>gp+qSokFg1(5mJQ)2ON! zT)m|#wasa;wKWTxfr7e#nXM_A6+@x4Lm?P{Lc{=~r5nS#o(Y$ttd=HVmawDB(X(0A zWIb=L3NffjZNF>YKvc9q7+MBsmWRPO%=!g4Cvbc(>lRqH&Pk~!;v)`YUluJ9_2Lc+ zUXS2u@Y2v1^`pzGGI3pAYCRi$#N$FtGc8e*Kr9-DYj>%tEO?Zl+avh`v|9o}Iw*S; zIsj4unoCJOMfA8uS(1y3Y&4(eI;ucq#+cIh!}oCPRgJNzBysvDYHQ!4$7o=KP$Wb}Mih9P9PbDL?O_ zfjcGgK3Vbu2~>t?;%44ZgQ^LmX`(L5V5}80&k)$z*jiC!)M5t7iG~U6gW2b-oXl#< zsb&=0rg*>wQL=MSShB+xK->+jwP{r?%Vz5Q?Yuq&CI5Bi3y+Z>1sX2=ORkE&7ItFl zij@})Fp!23ay=)r2OD9EyWZJ0$PxfvDHx!ATtJ~^8Ac*JOFSL$ z1UZHE@sjB0D%<=n--cQgOQWKe26C9GtgO=TZrZu)%ZKma0HK-x|960H*v^$sVq;FM z{ zo#cjfjvjbd7=YEk=B2J%Q#X}TGXP7cT@lR2lvAt->%??pt4+6@pqdv%o|Re7EBRnS z#XcML!lTdL+~L6gnc~k;X@4Cj9Mhr0?3R+ zRe1km+I86NE!&`qriNPJAfL9_%H0t7D-TzEc+mf%8w;6;K9&j-;GTnG@n zNO0l#AX4T@FKy5=YwboE(8c(B)IT=5G}!l0Ktm{7oHEICAbhE zcoB=Mz4mG{(BS*XdHCY-spK66_|mdN8cfD1YVs2lH8o99|DJ=_S&B-wQ7s=Dp(x=| zidv?h+j{H6u(m z^~x@8X#c^3)7x)8+8X&yu&{w`Q)YV4gJo;>oc{csS6*&4yP`A72M)Ea*!OgL>51eQ z?pe7c)yj{(@x#Zv^}WyR>Ad=#;O2{qK6=Zs_q^?zdGO4&#!pS?2MId$=+6~Bo6 f{od|>%uy@yh1u5*_1|v4c^zB7F?wX(_C5avjhnx~ literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/cancel_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/cancel_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ff0728366276a86446b1146e7ca288b31877b2f6 GIT binary patch literal 15498 zcmeI3e{3AZ701_6(l~ByS)^^!R4to>RCb)b-MzcLAIte-$Ct(jTzs_+v4a|Ay*p=b za<{wLUFXhj8kv{~A%3FXnvp|L)1zsZ9^yu#R{THgIcO0eu)G# zd%tgNk1PTIpgHMo@6DUg!h2Q501l>x_28YlHKx{R;fOx|4Ys zUX~c0{T4-ib*b~Mp?zKWl1w`qI|PN164FzdN(?)GLoZa=m)hmMv#?-*)hp#mh3sPn`OJ zk+{5Ix^2g$sm7J#Mu*4{JZv4 z19kq|dp>;rLd$}wk-tq`SO3Z6u3xOKzj5q8+P=N?>bB{1hgQ6>oEzW$MEkj#UyJJ5 zwlMYBmjAMC%`aW4Z)!gKZu>4OdHTIw+`lOAOFv%34cm{`Y>SEAgGYOq-#!3h&pov8 zC*kh`afM>8ZCHB$_ntU-(0$+Z;u8bMUe!eN@V5RRy?xm`ap6zr{&{cXSl{TH&F#V7 z3vaBx>)|&poS)2ot3j7WyMOsSwoVy1DhTIgR0i*dgr<0E?QMuU7LhB7dJY(0Htc%WdvA4In+MNOEzIcM} z&`m%KZa*V){s0{axp^TF@CDY;9*!4S&dYi^#^V)zoapn=g^w#z2mgglB`J1Czh4** zTCJ`j+crd&&15p}jMuH3gDf8kg;>tRdOQs5!B``jEoB+ax+Wi_7$*uW*;EZ%)iv6Q zD-GzwcB{+f3{?6Q=9M-|18G))9i+%+C4=SN96N_79>>el=~ zF#y{QEPdFN!TOy*v#*)$CKS08#~3zKIX;Taf)q$Yi3M+-pY;}u;zldUt8Sy@o^$rL znk+9)mDcghDFaz`4w>W0%W|oZU!gF}Ke59ElC7J)x}J*Uv#eYuV3lyPif-DZYKoq* zn!_wADX(`q8i-0Zh(KkK;RKkBy<9-#J)$qz!to-^|vRrGP@qQJqtK=%5RA)bK~@G!ohPhljlvv6F}9{`+3@du^i z5VM4tF;u5&!E73-$TFB~xysW6c2u_(9VsXfU_}MUB#VQDmkT!;%IUkT$E&| z_C#bSeSs2Yt6IyqRZdfVwAA)@R3i;e)WQKr-OaFYRT(7xgc&lQ zotckXEknbHS*KQ~)GuHmENSOwTZZ}VsxQSdl(SeKlC(jfMA%{i7aQica{2YF-dV-n zI&&w66Yof%y78|b?7PGjOz-Bz)~|?^QxRCKEd<2>U;SZrY8OSw(XhuIlzX#YN^XrzX7{R18jepuf5El4Zf zsI(t1t#qT(vhpTPwIiOwXh?H~%6SlrIi@}E%m^ZWLGTJ;7Hviq2{j8+eNh!YE-a_) z@xFovb<8U4pG#QbC4T@Fu~9=Yx0&E(8ePB)IT=5HG=n z0KuCC7oHE|CAbhEc$475^Fh1>7Xk!t5?pvbh?n3(fZ$Dn3(p7f5?lxnyh(83`5<0` z3ju;R2`)Sz#7l4?K=3BPh3A8K2`&T(-Xys2d=M|eg#f{u1Q(tU;w88cAb1mttFHVe zDA3^RpBea`=efyY6}}@%%bneEiW*%-QDfs2_0crEUZkk*TB_z7eem_tpHtK_{p{we z?}L5sjYZpgv#;&{Dc>;8H7>kTdu?6U(z@M8yJGDJE|0nH-kUx3*pXE?1Q$PJym|D< z`>%X^;geI%%m2J4yMe3SvZuNBo3}1Gw&&$rC&TidRka7Uo?iC&qQR!O9{geBosBIk zmkcZtgb!{yefJ+;yt)1ME7PksO)dD&RmT@h9NK#F(#~y9wd|jG=c0RT>h)Wm?f>7i e{r`Var=GrRUw?Gp?Z=#VU1RGvMkm&7-Sa6?V+wQt literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/confirm_hover_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/confirm_hover_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e5b719965807d8ea42a5758b5d6995652f3612b6 GIT binary patch literal 15951 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 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/confirm_locked_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/confirm_locked_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d6300bc3e9ac21a1f6ce5fab8363f1307935b0fe GIT binary patch literal 15950 zcmeI3du$xV9mm%p)WOB61*OoaD$9i;CuVOScYBZJe719!#-?0i&W6~bin6ymXK#GB zd)Zy*&Tc>f7x8G781X2DJ`}<$fh1I=RVCUcQCyP<1T+vrqd=k*niNW_K2VEF>dxN# z#^+HaQdLNE(mj6jo9}OSKJ%O3?ESI#^sZdd+_b2PqNwJc?r0o-U*fzMd=7rz{K}Tq z@av*X_n<{lpTE?3H&BoK;0lUr@M)1qZ||^S8`iLqp?e|`I%671Ed?klcWl>|pIm>S z_lupce&?}My>0V1WODbnw*LJ6b<>YC%Uhm3a@Cv+SH zl6JH+L_M+g&ur(CgJ+vtmmK|j*Cs0Y>PMTnKTw{7w_eDN+B+K7_ek-fU-dE1JOp}P zcy#`Kp|1jIk!p4vuD<J2w zA8P#r_CV|gNiouHSv3;Col9pRZ;A?a*a3HruY_ABEfN>FumgquP+2?j_5Tc&gAlx|76c2_>G1b;ittc%Wfu}9inUCw~? zK&+RJ7$%@aw~tXcpP%*z-Mr}cd;Qna0>_Ii=V3h@BX}e)CwT?B_;Q7t;IELWCZ%}v z>&4-q)$SU$?To~-*=*LG^|%dlh~GoG?c1c9+W;sQfC(AOFr2$v(^M(Rt3rTRRmK|xGNXFEDwBYi zm_#Bd1U#ak@`9M3WUYKw=%}HLI*9~B7dF0XC@_3EJ*0rhCl!?kj0i-I;du~XWS{6~ z6j@M3QBhRR<0%bP6>Qc(t4s~nB{@~q$C-=j%mrS(;1~Q1pY$dff%E#9gkSV9fKSMx zPgVRt0i_}8gqbx|w`Rd?8mr1Om}~jU(+4)xww4?zEnh)0vS|TlW460WJF{GJ$`!@Y z zplb+b$hqvyLeyFr8a~82wK}E!4;I3bH-5ThP!#~YN-)6qgg}x}RhWsg2kxqXCnzL2 zpQy-wp|s4O@nxuFu`(>{LqH9)rJ0o)K1(ZiZuRg}ZlLPQ|Nq-S=WXQ*JF&JVmg?;6 z8X8g+DQTE#*$!)Ic?hr>eJI3M8Y|fns+=AXBW0K|Lj_@YNYrbttAc+GdJF#yibW^B zVj0~E?+OF(^shvz>Q)uTxjT{IF#$`KoG;IOh@I7j_8)M{eQGq{-!E|R#q!Q)QCjUr zrGt2BwHuXIlsD;`9fqYPf1yZomCCt=lJZRZ;N3n5`$W+Lk9o8iRrG2(kQ#_;@SwMx z1B(w7HK?PmaBwbRm7^#uWK`9CdA`UOTH#$k{KLVP|8dN!|K(%yeEz7+=zK;+Vv#`* zAb6AD!t+7A1Q!AXZxUR1K8Tm#LV(~+f(y?F@e*7J5WGon;rSq5f(rqHHwi90AH+*= zAwcjZ!G-68cnK~92;L;P@O%(2!G!?9n* z*IYRX`+mPC+SQ*s_VOEhnoNXHtM+`i_@hI`{9x9{2d zr)`bgyld`w*rR@7^zd`@xy9MWcT>-Ay-VA3%P?aQt3JaF58A$?cMJE%jK543-K;(0=*cLlO}@L&F);X=5BYp zyXjr>pvCAzQ85ZCiilzru_A(0kShM6V4t*46>OjMp#h0~k|NYUv%8nO-(Bu9t@LpY z?)I0N@Ao&epZU#XU-s1a*x@bRySfoVTSiB+6Xd$h{dV0+esBD6=nA>qZH`Ra2;H;Y z{dStXPcDt+m8E$lq4I<2?qo$G2};kPxZb^W^EwduCgN(g^Fwnp`a+VGO0?DB)L~9V`14bth`|q(|(nW zuOs9#8Lz6i2ajsHYLx8*Nuk+NU+;Q0oRuA%CiOTF5M!hm^P(t45>hz!uo#j=5hU@I zO*wLgsudPnA!Q%7VLFs;SjmObzTCQa@^G zpH|ITwQ2u%0sZ{I-hPbo9M(76P<|>;)jo-B6r8eb;h|Yl6F0Nh{j80)=JZLyJ^hPX z<3jkYjq9^su^ZPj=^knlsRc4G*;@m$c{a4wNA~qo?wS@CJbgSYl8+E_f2&s5-E5uS z+zPv!t##*Py5^+GKJ+Hq)C^qi$?G2rljI=*r~6~ENGvHdH#a-RwF)jyWi_&YZMP~z z165CRM@!>$J0s|*8vUgL|IWRY^jpcp0(njmY7Z)ni(K0Xw>hZa(QQr))3HGy4QkAA zQ4v7_!v$$jV}^^02nrZ3NP`+PTvS9*z;Hnt)R^I-B7y>j3(}y*3>Os<6fj(n1~q25 zsED9|;es@%F~dbg1O*Hiq(O}tE-E4@V7MR+YRqs^5kUdN1!+)YhKq^_3K%X(gBmkj zR76m~a6uZ>nBk%#f&zvM(xAo+7Zni{FkFxZHDIYL5&$M zDk3OgxF8K`%y3Z=K>@=BX;5Q^i;4&e7%oVI8Z%r}L{PwRK^oL}Q(WEkSO2h1-uEk$ z7yF(*Jb9A56v!zf6FG#I`Vcy~jL={IlIto$6J4ld#}q=+8wl+*etvw<3emnYnjOkl ze);6p)Yk9Cp68!TEPv4R=CRekg>NtNf2{OzQmS-qY( z(6wvfwJ%@PF5%Db-9A3@(YaHdN6zfee%Nt#x$mKi7mSq)`FHj2zP{f1=h=UrcwqWq z^rz4E&)!(-V`A365yf0||B>Hq)$ literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/fill_color_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/fill_color_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5c209a576ae71191b37440ccac121545525db6e8 GIT binary patch literal 15128 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 FV1i1>W<>x1 literal 0 HcmV?d00001