diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java index f6e4e77f27..7846807341 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java @@ -67,6 +67,9 @@ class XpInfoBox extends JPanel private static final String HTML_LABEL_TEMPLATE = "%s%s"; + private static final String REMOVE_STATE = "Remove from canvas"; + private static final String ADD_STATE = "Add to canvas"; + // Instance members private final JPanel panel; @@ -89,6 +92,7 @@ class XpInfoBox extends JPanel private final JLabel expLeft = new JLabel(); private final JLabel actionsLeft = new JLabel(); private final JMenuItem pauseSkill = new JMenuItem("Pause"); + private final JMenuItem canvasItem = new JMenuItem(ADD_STATE); private final XpTrackerConfig xpTrackerConfig; @@ -128,6 +132,21 @@ class XpInfoBox extends JPanel popupMenu.add(reset); popupMenu.add(resetOthers); popupMenu.add(pauseSkill); + popupMenu.add(canvasItem); + + canvasItem.addActionListener(e -> + { + if (canvasItem.getText().equals(REMOVE_STATE)) + { + xpTrackerPlugin.removeOverlay(skill); + canvasItem.setText(ADD_STATE); + } + else + { + xpTrackerPlugin.addOverlay(skill); + canvasItem.setText(REMOVE_STATE); + } + }); JLabel skillIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(skill))); skillIcon.setHorizontalAlignment(SwingConstants.CENTER); @@ -177,6 +196,7 @@ class XpInfoBox extends JPanel void reset() { + canvasItem.setText(ADD_STATE); container.remove(statsPanel); panel.remove(this); panel.revalidate(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBoxOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBoxOverlay.java new file mode 100644 index 0000000000..99c5880901 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBoxOverlay.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.xptracker; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.Skill; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.components.ComponentOrientation; +import net.runelite.client.ui.overlay.components.ImageComponent; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.components.ProgressBarComponent; +import net.runelite.client.ui.overlay.components.SplitComponent; +import net.runelite.client.util.StackFormatter; + +class XpInfoBoxOverlay extends Overlay +{ + private static final int PANEL_PREFERRED_WIDTH = 150; + private static final int BORDER_SIZE = 2; + private static final int XP_AND_PROGRESS_BAR_GAP = 2; + private static final int XP_AND_ICON_GAP = 4; + private static final Rectangle XP_AND_ICON_COMPONENT_BORDER = new Rectangle(2, 1, 4, 0); + + private final PanelComponent panel = new PanelComponent(); + private final PanelComponent iconXpSplitPanel = new PanelComponent(); + private final XpTrackerPlugin plugin; + + @Getter(AccessLevel.PACKAGE) + private final Skill skill; + private final BufferedImage icon; + + XpInfoBoxOverlay( + XpTrackerPlugin plugin, + Skill skill, + BufferedImage icon) + { + super(plugin); + this.plugin = plugin; + this.skill = skill; + this.icon = icon; + panel.setBorder(new Rectangle(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE)); + panel.setGap(new Point(0, XP_AND_PROGRESS_BAR_GAP)); + panel.setPreferredSize(new Dimension(PANEL_PREFERRED_WIDTH, 0)); + iconXpSplitPanel.setBorder(XP_AND_ICON_COMPONENT_BORDER); + iconXpSplitPanel.setBackgroundColor(null); + iconXpSplitPanel.setPreferredSize(new Dimension(PANEL_PREFERRED_WIDTH, 0)); + } + + @Override + public Dimension render(Graphics2D graphics) + { + panel.getChildren().clear(); + iconXpSplitPanel.getChildren().clear(); + + //Setting the font to rs small font so that the overlay isn't huge + graphics.setFont(FontManager.getRunescapeSmallFont()); + + final XpSnapshotSingle snapshot = plugin.getSkillSnapshot(skill); + + final LineComponent xpLine = LineComponent.builder() + .left("XP Gained:") + .right(StackFormatter.quantityToRSDecimalStack(snapshot.getXpGainedInSession())) + .build(); + + final LineComponent xpHour = LineComponent.builder() + .left("XP/Hour:") + .right(StackFormatter.quantityToRSDecimalStack(snapshot.getXpPerHour())) + .build(); + + final SplitComponent xpSplit = SplitComponent.builder() + .first(xpLine) + .second(xpHour) + .orientation(ComponentOrientation.VERTICAL) + .build(); + + final ImageComponent imageComponent = new ImageComponent(icon); + final SplitComponent iconXpSplit = SplitComponent.builder() + .first(imageComponent) + .second(xpSplit) + .orientation(ComponentOrientation.HORIZONTAL) + .gap(new Point(XP_AND_ICON_GAP, 0)) + .build(); + + iconXpSplitPanel.getChildren().add(iconXpSplit); + + final ProgressBarComponent progressBarComponent = new ProgressBarComponent(); + progressBarComponent.setValue(snapshot.getSkillProgressToGoal()); + + panel.getChildren().add(iconXpSplitPanel); + panel.getChildren().add(progressBarComponent); + + return panel.render(graphics); + } + + @Override + public String getName() + { + return super.getName() + skill.getName(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java index a8d65962a5..8f08b0ba7b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java @@ -59,6 +59,7 @@ import static net.runelite.client.plugins.xptracker.XpWorldType.NORMAL; import net.runelite.client.task.Schedule; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.util.ImageUtil; import net.runelite.http.api.xp.XpClient; @@ -98,6 +99,9 @@ public class XpTrackerPlugin extends Plugin @Inject private NPCManager npcManager; + @Inject + private OverlayManager overlayManager; + private NavigationButton navButton; private XpPanel xpPanel; private XpWorldType lastWorldType; @@ -142,6 +146,7 @@ public class XpTrackerPlugin extends Plugin @Override protected void shutDown() throws Exception { + overlayManager.removeIf(e -> e instanceof XpInfoBoxOverlay); xpState.reset(); clientToolbar.removeNavigation(navButton); } @@ -208,6 +213,27 @@ public class XpTrackerPlugin extends Plugin return xpType; } + /** + * Adds an overlay to the canvas for tracking a specific skill. + * + * @param skill the skill for which the overlay should be added + */ + void addOverlay(Skill skill) + { + removeOverlay(skill); + overlayManager.add(new XpInfoBoxOverlay(this, skill, skillIconManager.getSkillImage(skill))); + } + + /** + * Removes an overlay from the overlayManager if it's present. + * + * @param skill the skill for which the overlay should be removed. + */ + void removeOverlay(Skill skill) + { + overlayManager.removeIf(e -> e instanceof XpInfoBoxOverlay && ((XpInfoBoxOverlay) e).getSkill() == skill); + } + /** * Reset internal state and re-initialize all skills with XP currently cached by the RS client * This is called by the user manually clicking resetSkillState in the UI. @@ -230,6 +256,7 @@ public class XpTrackerPlugin extends Plugin } xpState.initializeSkill(skill, currentXp); + removeOverlay(skill); } } @@ -242,6 +269,7 @@ public class XpTrackerPlugin extends Plugin xpState.reset(); xpPanel.resetAllInfoBoxes(); xpPanel.updateTotal(new XpSnapshotSingle.XpSnapshotSingleBuilder().build()); + overlayManager.removeIf(e -> e instanceof XpInfoBoxOverlay); } /** @@ -254,6 +282,7 @@ public class XpTrackerPlugin extends Plugin int currentXp = client.getSkillExperience(skill); xpState.resetSkill(skill, currentXp); xpPanel.resetSkill(skill); + removeOverlay(skill); } /** @@ -272,7 +301,6 @@ public class XpTrackerPlugin extends Plugin } } - @Subscribe public void onExperienceChanged(ExperienceChanged event) { diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/SplitComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/SplitComponent.java index 2e77a7dde2..75ab98f137 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/SplitComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/SplitComponent.java @@ -58,9 +58,8 @@ public class SplitComponent implements LayoutableRenderableEntity @Override public Dimension render(Graphics2D graphics) { - graphics.translate(preferredLocation.x, preferredLocation.y); + first.setPreferredLocation(preferredLocation); first.setPreferredSize(preferredSize); - first.setPreferredLocation(new Point(0, 0)); final Dimension firstDimension = first.render(graphics); int x = 0, y = 0; @@ -74,7 +73,7 @@ public class SplitComponent implements LayoutableRenderableEntity x = firstDimension.width + gap.x; } - second.setPreferredLocation(new Point(x, y)); + second.setPreferredLocation(new Point(x + preferredLocation.x, y + preferredLocation.y)); // Make the second component fit to whatever size is left after the first component is rendered second.setPreferredSize(new Dimension(preferredSize.width - x, preferredSize.height - y)); @@ -95,8 +94,6 @@ public class SplitComponent implements LayoutableRenderableEntity totalWidth = x + secondDimension.width; } - graphics.translate(-preferredLocation.x, -preferredLocation.y); - final Dimension dimension = new Dimension(totalWidth, totalHeight); bounds.setLocation(preferredLocation); bounds.setSize(dimension);