Merge pull request #3028 from Adam-/screenmarkers

Add screen markers plugin
This commit is contained in:
Adam
2018-05-22 19:38:46 -04:00
committed by GitHub
12 changed files with 1067 additions and 25 deletions

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2018, Kamiel, <https://github.com/Kamielvf>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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;
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2018, Kamiel, <https://github.com/Kamielvf>
* 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);
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2018, Kamiel, <https://github.com/Kamielvf>
* 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;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2018, Kamiel, <https://github.com/Kamielvf>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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);
}
}

View File

@@ -0,0 +1,254 @@
/*
* Copyright (c) 2018, Kamiel, <https://github.com/Kamielvf>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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<ScreenMarkerOverlay> screenMarkers = new ArrayList<>();
@Getter(AccessLevel.PACKAGE)
private ScreenMarker currentMarker;
@Getter
private boolean creatingScreenMarker = false;
@Override
public Collection<Overlay> getOverlays()
{
final List<Overlay> 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<ScreenMarkerOverlay> loadConfig(String json)
{
if (Strings.isNullOrEmpty(json))
{
return Stream.empty();
}
final Gson gson = new Gson();
final List<ScreenMarker> screenMarkerData = gson.fromJson(json, new TypeToken<ArrayList<ScreenMarker>>()
{
}.getType());
return screenMarkerData.stream().map(ScreenMarkerOverlay::new);
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2018, Kamiel, <https://github.com/Kamielvf>
* 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;
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2018, Kamiel, <https://github.com/Kamielvf>
* 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);
}
}

View File

@@ -0,0 +1,211 @@
/*
* Copyright (c) 2018, Kamiel, <https://github.com/Kamielvf>
* 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;
}
}

View File

@@ -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();
}
}

View File

@@ -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
*/

View File

@@ -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<Client> 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<Overlay> 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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B