Merge pull request #4114 from LeviSchuck/xp-pausing

Add pause skill timing feature to the XP Tracker
This commit is contained in:
Adam
2018-07-18 20:36:34 -04:00
committed by GitHub
10 changed files with 520 additions and 47 deletions

View File

@@ -54,6 +54,15 @@ import net.runelite.client.util.SwingUtil;
@Slf4j
class XpInfoBox extends JPanel
{
// Templates
private static final String HTML_TOOL_TIP_TEMPLATE =
"<html>%s actions done<br/>"
+ "%s actions/hr<br/>"
+ "%s till goal lvl</html>";
private static final String HTML_LABEL_TEMPLATE =
"<html><body style='color:%s'>%s<span style='color:white'>%s</span></body></html>";
// Instance members
private final JPanel panel;
@Getter(AccessLevel.PACKAGE)
@@ -74,6 +83,9 @@ class XpInfoBox extends JPanel
private final JLabel expHour = new JLabel();
private final JLabel expLeft = new JLabel();
private final JLabel actionsLeft = new JLabel();
private final JMenuItem pauseSkill = new JMenuItem("Pause");
private boolean paused = false;
XpInfoBox(XpTrackerPlugin xpTrackerPlugin, Client client, JPanel panel, Skill skill, SkillIconManager iconManager) throws IOException
{
@@ -98,12 +110,16 @@ class XpInfoBox extends JPanel
final JMenuItem resetOthers = new JMenuItem("Reset others");
resetOthers.addActionListener(e -> xpTrackerPlugin.resetOtherSkillState(skill));
// Create reset others menu
pauseSkill.addActionListener(e -> xpTrackerPlugin.pauseSkill(skill, !paused));
// Create popup menu
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
popupMenu.add(openXpTracker);
popupMenu.add(reset);
popupMenu.add(resetOthers);
popupMenu.add(pauseSkill);
JLabel skillIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(skill)));
skillIcon.setHorizontalAlignment(SwingConstants.CENTER);
@@ -138,6 +154,7 @@ class XpInfoBox extends JPanel
progressBar.setMaximumValue(100);
progressBar.setBackground(new Color(61, 56, 49));
progressBar.setForeground(SkillColor.values()[skill.ordinal()].getColor());
progressBar.setDimmedText("Paused");
progressWrapper.add(progressBar, BorderLayout.NORTH);
@@ -157,12 +174,12 @@ class XpInfoBox extends JPanel
panel.revalidate();
}
void update(boolean updated, XpSnapshotSingle xpSnapshotSingle)
void update(boolean updated, boolean paused, XpSnapshotSingle xpSnapshotSingle)
{
SwingUtilities.invokeLater(() -> rebuildAsync(updated, xpSnapshotSingle));
SwingUtilities.invokeLater(() -> rebuildAsync(updated, paused, xpSnapshotSingle));
}
private void rebuildAsync(boolean updated, XpSnapshotSingle xpSnapshotSingle)
private void rebuildAsync(boolean updated, boolean skillPaused, XpSnapshotSingle xpSnapshotSingle)
{
if (updated)
{
@@ -172,6 +189,8 @@ class XpInfoBox extends JPanel
panel.revalidate();
}
paused = skillPaused;
// Update information labels
expGained.setText(htmlLabel("XP Gained: ", xpSnapshotSingle.getXpGainedInSession()));
expLeft.setText(htmlLabel("XP Left: ", xpSnapshotSingle.getXpRemainingToGoal()));
@@ -181,28 +200,42 @@ class XpInfoBox extends JPanel
progressBar.setValue(xpSnapshotSingle.getSkillProgressToGoal());
progressBar.setCenterLabel(xpSnapshotSingle.getSkillProgressToGoal() + "%");
progressBar.setLeftLabel("Lvl. " + xpSnapshotSingle.getStartLevel());
progressBar.setRightLabel("Lvl. " + (xpSnapshotSingle.getEndLevel()));
progressBar.setRightLabel("Lvl. " + xpSnapshotSingle.getEndLevel());
progressBar.setToolTipText("<html>"
+ xpSnapshotSingle.getActionsInSession() + " actions done"
+ "<br/>"
+ xpSnapshotSingle.getActionsPerHour() + " actions/hr"
+ "<br/>"
+ xpSnapshotSingle.getTimeTillGoal() + " till goal lvl"
+ "</html>");
progressBar.setToolTipText(String.format(
HTML_TOOL_TIP_TEMPLATE,
xpSnapshotSingle.getActionsInSession(),
xpSnapshotSingle.getActionsPerHour(),
xpSnapshotSingle.getTimeTillGoal()));
progressBar.setDimmed(skillPaused);
progressBar.repaint();
}
else if (!paused && skillPaused)
{
// React to the skill state now being paused
progressBar.setDimmed(true);
progressBar.repaint();
paused = true;
pauseSkill.setText("Unpause");
}
else if (paused && !skillPaused)
{
// React to the skill being unpaused (without update)
progressBar.setDimmed(false);
progressBar.repaint();
paused = false;
pauseSkill.setText("Pause");
}
// Update exp per hour seperately, everytime (not only when there's an update)
// Update exp per hour separately, every time (not only when there's an update)
expHour.setText(htmlLabel("XP/Hour: ", xpSnapshotSingle.getXpPerHour()));
}
static String htmlLabel(String key, int value)
{
String valueStr = StackFormatter.quantityToRSDecimalStack(value);
return "<html><body style = 'color:" + SwingUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR) + "'>" + key + "<span style = 'color:white'>" + valueStr + "</span></body></html>";
return String.format(HTML_LABEL_TEMPLATE, SwingUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr);
}
}

View File

@@ -172,13 +172,13 @@ class XpPanel extends PluginPanel
}
}
void updateSkillExperience(boolean updated, Skill skill, XpSnapshotSingle xpSnapshotSingle)
void updateSkillExperience(boolean updated, boolean paused, Skill skill, XpSnapshotSingle xpSnapshotSingle)
{
final XpInfoBox xpInfoBox = infoBoxes.get(skill);
if (xpInfoBox != null)
{
xpInfoBox.update(updated, xpSnapshotSingle);
xpInfoBox.update(updated, paused, xpSnapshotSingle);
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (c) 2018, Levi <me@levischuck.com>
* 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.util.EnumMap;
import java.util.Map;
import net.runelite.api.Skill;
class XpPauseState
{
// Internal state
private final Map<Skill, XpPauseStateSingle> skillPauses = new EnumMap<>(Skill.class);
private boolean cachedIsLoggedIn = false;
boolean pauseSkill(Skill skill)
{
return findPauseState(skill).manualPause();
}
boolean unpauseSkill(Skill skill)
{
return findPauseState(skill).unpause();
}
boolean isPaused(Skill skill)
{
return findPauseState(skill).isPaused();
}
void tickXp(Skill skill, int currentXp, int pauseAfterMinutes)
{
final XpPauseStateSingle state = findPauseState(skill);
if (state.getXp() != currentXp)
{
state.xpChanged(currentXp);
}
else if (pauseAfterMinutes > 0)
{
final long now = System.currentTimeMillis();
final int pauseAfterMillis = pauseAfterMinutes * 60 * 1000;
final long lastChangeMillis = state.getLastChangeMillis();
// When config.pauseSkillAfter is 0, it is effectively disabled
if (lastChangeMillis != 0 && (now - lastChangeMillis) >= pauseAfterMillis)
{
state.timeout();
}
}
}
void tickLogout(boolean pauseOnLogout, boolean loggedIn)
{
// Deduplicated login and logout calls
if (!cachedIsLoggedIn && loggedIn)
{
cachedIsLoggedIn = true;
for (Skill skill : Skill.values())
{
findPauseState(skill).login();
}
}
else if (cachedIsLoggedIn && !loggedIn)
{
cachedIsLoggedIn = false;
// If configured, then let the pause state know to pause with reason: logout
if (pauseOnLogout)
{
for (Skill skill : Skill.values())
{
findPauseState(skill).logout();
}
}
}
}
private XpPauseStateSingle findPauseState(Skill skill)
{
return skillPauses.computeIfAbsent(skill, XpPauseStateSingle::new);
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (c) 2018, Levi <me@levischuck.com>
* 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.util.EnumSet;
import java.util.Set;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.api.Skill;
@RequiredArgsConstructor
class XpPauseStateSingle
{
@Getter
private final Skill skill;
private final Set<XpPauseReason> pauseReasons = EnumSet.noneOf(XpPauseReason.class);
@Getter
private long lastChangeMillis;
@Getter
private int xp;
boolean isPaused()
{
return !pauseReasons.isEmpty();
}
boolean login()
{
return pauseReasons.remove(XpPauseReason.PAUSED_LOGOUT);
}
boolean logout()
{
return pauseReasons.add(XpPauseReason.PAUSED_LOGOUT);
}
boolean timeout()
{
return pauseReasons.add(XpPauseReason.PAUSED_TIMEOUT);
}
boolean manualPause()
{
return pauseReasons.add(XpPauseReason.PAUSE_MANUAL);
}
boolean xpChanged(int xp)
{
this.xp = xp;
this.lastChangeMillis = System.currentTimeMillis();
return clearAll();
}
boolean unpause()
{
this.lastChangeMillis = System.currentTimeMillis();
return clearAll();
}
private boolean clearAll()
{
if (pauseReasons.isEmpty())
{
return false;
}
pauseReasons.clear();
return true;
}
private enum XpPauseReason
{
PAUSE_MANUAL,
PAUSED_LOGOUT,
PAUSED_TIMEOUT
}
}

View File

@@ -120,6 +120,11 @@ class XpState
}
}
void tick(Skill skill, long delta)
{
getSkill(skill).tick(delta);
}
/**
* Forcefully initialize a skill with a known start XP from the current XP.
* This is used in resetAndInitState by the plugin. It should not result in showing the XP in the UI.

View File

@@ -25,8 +25,6 @@
*/
package net.runelite.client.plugins.xptracker;
import java.time.Duration;
import java.time.Instant;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -45,7 +43,7 @@ class XpStateSingle
@Getter
private int xpGained = 0;
private Instant skillTimeStart = null;
private long skillTime = 0;
private int actions = 0;
private int startLevelExp = 0;
private int endLevelExp = 0;
@@ -65,7 +63,7 @@ class XpStateSingle
private int toHourly(int value)
{
if (skillTimeStart == null)
if (skillTime == 0)
{
return 0;
}
@@ -75,7 +73,7 @@ class XpStateSingle
private long getTimeElapsedInSeconds()
{
if (skillTimeStart == null)
if (skillTime == 0)
{
return 0;
}
@@ -84,7 +82,7 @@ class XpStateSingle
// To prevent that, pretend the skill has been active for a minute (60 seconds)
// This will create a lower estimate for the first minute,
// but it isn't ridiculous like saying 2 billion XP per hour.
return Math.max(60, Duration.between(skillTimeStart, Instant.now()).getSeconds());
return Math.max(60, skillTime / 1000);
}
private int getXpRemaining()
@@ -229,15 +227,14 @@ class XpStateSingle
endLevelExp = goalEndXp;
}
// If this is first time we are updating, we just started tracking
if (skillTimeStart == null)
{
skillTimeStart = Instant.now();
}
return true;
}
public void tick(long delta)
{
skillTime += delta;
}
XpSnapshotSingle snapshot()
{
return XpSnapshotSingle.builder()

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2018, Levi <me@levischuck.com>
* 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 net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup("xpTracker")
public interface XpTrackerConfig extends Config
{
@ConfigItem(
position = 0,
keyName = "logoutPausing",
name = "Pause on Logout",
description = "Configures whether skills should pause on logout"
)
default boolean pauseOnLogout()
{
return false;
}
@ConfigItem(
position = 1,
keyName = "pauseSkillAfter",
name = "Auto pause after",
description = "Configures how many minutes passes before pausing a skill while in game and there's no XP, 0 means disabled"
)
default int pauseSkillAfter()
{
return 0;
}
}

View File

@@ -28,7 +28,9 @@ package net.runelite.client.plugins.xptracker;
import static com.google.common.base.MoreObjects.firstNonNull;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Binder;
import com.google.inject.Provides;
import java.awt.image.BufferedImage;
import java.time.temporal.ChronoUnit;
import java.util.EnumSet;
import java.util.Objects;
import javax.imageio.ImageIO;
@@ -43,10 +45,12 @@ import net.runelite.api.WorldType;
import net.runelite.api.events.ExperienceChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.SkillIconManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import static net.runelite.client.plugins.xptracker.XpWorldType.NORMAL;
import net.runelite.client.task.Schedule;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.PluginToolbar;
import net.runelite.http.api.xp.XpClient;
@@ -68,15 +72,24 @@ public class XpTrackerPlugin extends Plugin
@Inject
private SkillIconManager skillIconManager;
@Inject
private XpTrackerConfig xpTrackerConfig;
private NavigationButton navButton;
private XpPanel xpPanel;
private final XpState xpState = new XpState();
private XpWorldType lastWorldType;
private String lastUsername;
private long lastTickMillis = 0;
private final XpClient xpClient = new XpClient();
private final XpState xpState = new XpState();
private final XpPauseState xpPauseState = new XpPauseState();
@Provides
XpTrackerConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(XpTrackerConfig.class);
}
@Override
public void configure(Binder binder)
@@ -229,7 +242,7 @@ public class XpTrackerPlugin extends Plugin
final XpUpdateResult updateResult = xpState.updateSkill(skill, currentXp, startGoalXp, endGoalXp);
final boolean updated = XpUpdateResult.UPDATED.equals(updateResult);
xpPanel.updateSkillExperience(updated, skill, xpState.getSkillSnapshot(skill));
xpPanel.updateSkillExperience(updated, xpPauseState.isPaused(skill), skill, xpState.getSkillSnapshot(skill));
xpState.recalculateTotal();
xpPanel.updateTotal(xpState.getTotalSnapshot());
}
@@ -237,14 +250,7 @@ public class XpTrackerPlugin extends Plugin
@Subscribe
public void onGameTick(GameTick event)
{
// Rebuild calculated values like xp/hr in panel
for (Skill skill : Skill.values())
{
xpPanel.updateSkillExperience(false, skill, xpState.getSkillSnapshot(skill));
}
xpState.recalculateTotal();
xpPanel.updateTotal(xpState.getTotalSnapshot());
rebuildSkills();
}
XpSnapshotSingle getSkillSnapshot(Skill skill)
@@ -361,4 +367,59 @@ public class XpTrackerPlugin extends Plugin
return null;
}
}
@Schedule(
period = 1,
unit = ChronoUnit.SECONDS
)
public void tickSkillTimes()
{
// Adjust unpause states
for (Skill skill : Skill.values())
{
xpPauseState.tickXp(skill, client.getSkillExperience(skill), xpTrackerConfig.pauseSkillAfter());
}
xpPauseState.tickLogout(xpTrackerConfig.pauseOnLogout(), !GameState.LOGIN_SCREEN.equals(client.getGameState()));
if (lastTickMillis == 0)
{
lastTickMillis = System.currentTimeMillis();
return;
}
final long nowMillis = System.currentTimeMillis();
final long tickDelta = nowMillis - lastTickMillis;
lastTickMillis = nowMillis;
for (Skill skill : Skill.values())
{
if (!xpPauseState.isPaused(skill))
{
xpState.tick(skill, tickDelta);
}
}
rebuildSkills();
}
private void rebuildSkills()
{
// Rebuild calculated values like xp/hr in panel
for (Skill skill : Skill.values())
{
xpPanel.updateSkillExperience(false, xpPauseState.isPaused(skill), skill, xpState.getSkillSnapshot(skill));
}
xpState.recalculateTotal();
xpPanel.updateTotal(xpState.getTotalSnapshot());
}
void pauseSkill(Skill skill, boolean pause)
{
if (pause ? xpPauseState.pauseSkill(skill) : xpPauseState.unpauseSkill(skill))
{
xpPanel.updateSkillExperience(false, xpPauseState.isPaused(skill), skill, xpState.getSkillSnapshot(skill));
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2018, Levi <me@levischuck.com>
* 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.ui.components;
import java.awt.Color;
import javax.swing.JPanel;
import lombok.Getter;
public class DimmableJPanel extends JPanel
{
// Dimming state, allows for restoring original colors before dimming
@Getter
private boolean dimmed = false;
private Color dimmedForeground = null;
private Color dimmedBackground = null;
private Color undimmedForeground = null;
private Color undimmedBackground = null;
@Override
public void setForeground(Color color)
{
undimmedForeground = color;
dimmedForeground = color.darker();
super.setForeground(color);
}
@Override
public void setBackground(Color color)
{
undimmedBackground = color;
dimmedBackground = color.darker();
super.setBackground(color);
}
@Override
public Color getForeground()
{
return dimmed ? dimmedForeground : undimmedForeground;
}
@Override
public Color getBackground()
{
return dimmed ? dimmedBackground : undimmedBackground;
}
/**
* Dimming sets all parts of this component with darker colors except for the central label
* This is useful for showing that progress is paused
* Setting dim to false will restore the original colors from before the component was dimmed.
* @param dimmed
*/
public void setDimmed(boolean dimmed)
{
this.dimmed = dimmed;
if (dimmed)
{
super.setBackground(dimmedBackground);
super.setForeground(dimmedForeground);
}
else
{
super.setBackground(undimmedBackground);
super.setForeground(undimmedForeground);
}
}
}

View File

@@ -29,7 +29,6 @@ import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import lombok.Setter;
@@ -39,7 +38,7 @@ import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
/**
* A progress bar to be displayed underneath the GE offer item panels
*/
public class ProgressBar extends JPanel
public class ProgressBar extends DimmableJPanel
{
@Setter
private int maximumValue;
@@ -50,11 +49,16 @@ public class ProgressBar extends JPanel
private final JLabel leftLabel = new JShadowedLabel();
private final JLabel rightLabel = new JShadowedLabel();
private final JLabel centerLabel = new JShadowedLabel();
private String centerLabelText = "";
private String dimmedText = "";
public ProgressBar()
{
setLayout(new BorderLayout());
// The background color should be overridden
setBackground(Color.GREEN.darker());
setForeground(Color.GREEN.brighter());
setPreferredSize(new Dimension(100, 16));
leftLabel.setFont(FontManager.getRunescapeSmallFont());
@@ -70,10 +74,10 @@ public class ProgressBar extends JPanel
centerLabel.setHorizontalAlignment(SwingConstants.CENTER);
centerLabel.setBorder(new EmptyBorder(2, 0, 0, 0));
// Adds components to be automatically redrawn when paintComponents is called
add(leftLabel, BorderLayout.WEST);
add(centerLabel, BorderLayout.CENTER);
add(rightLabel, BorderLayout.EAST);
}
@Override
@@ -88,20 +92,45 @@ public class ProgressBar extends JPanel
super.paintComponents(g);
}
@Override
public void setDimmed(boolean dimmed)
{
super.setDimmed(dimmed);
if (dimmed)
{
leftLabel.setForeground(Color.GRAY);
rightLabel.setForeground(Color.GRAY);
centerLabel.setText(dimmedText);
}
else
{
leftLabel.setForeground(Color.WHITE);
rightLabel.setForeground(Color.WHITE);
centerLabel.setText(centerLabelText);
}
}
public void setLeftLabel(String txt)
{
this.leftLabel.setText(txt);
leftLabel.setText(txt);
}
public void setRightLabel(String txt)
{
this.rightLabel.setText(txt);
rightLabel.setText(txt);
}
public void setCenterLabel(String txt)
{
this.centerLabel.setText(txt);
centerLabelText = txt;
centerLabel.setText(isDimmed() ? dimmedText : txt);
}
public void setDimmedText(String txt)
{
dimmedText = txt;
centerLabel.setText(isDimmed() ? txt : centerLabelText);
}
public double getPercentage()