Merge pull request #1419 from psikoi/color-scheme-orange-dark

"RuneLite Obsidian" Client Redesign
This commit is contained in:
Tomas Slusny
2018-05-19 16:02:33 +02:00
committed by GitHub
94 changed files with 3770 additions and 1270 deletions

View File

@@ -77,12 +77,12 @@
<dependency>
<groupId>net.runelite.pushingpixels</groupId>
<artifactId>substance</artifactId>
<version>8.0.00-dev</version>
<version>8.0.02</version>
</dependency>
<dependency>
<groupId>net.runelite.pushingpixels</groupId>
<artifactId>trident</artifactId>
<version>1.4</version>
<version>1.5.00</version>
<scope>runtime</scope>
</dependency>
<dependency>

View File

@@ -41,6 +41,8 @@ public class RuneLiteProperties
private static final String RUNESCAPE_VERSION = "runescape.version";
private static final String DISCORD_APP_ID = "runelite.discord.appid";
private static final String DISCORD_INVITE = "runelite.discord.invite";
private static final String GITHUB_LINK = "runelite.github.link";
private static final String PATREON_LINK = "runelite.patreon.link";
private final Properties properties = new Properties();
@@ -82,4 +84,14 @@ public class RuneLiteProperties
{
return properties.getProperty(DISCORD_INVITE);
}
}
public String getGithubLink()
{
return properties.getProperty(GITHUB_LINK);
}
public String getPatreonLink()
{
return properties.getProperty(PATREON_LINK);
}
}

View File

@@ -37,7 +37,6 @@ 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 java.util.Comparator;
import java.util.Map;
@@ -61,6 +60,7 @@ import javax.swing.JTextField;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
@@ -75,16 +75,22 @@ import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginInstantiationException;
import net.runelite.client.plugins.PluginManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.DynamicGridLayout;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.ComboBoxListRenderer;
import net.runelite.client.ui.components.IconTextField;
@Slf4j
public class ConfigPanel extends PluginPanel
{
private static final int TEXT_FIELD_WIDTH = 7;
private static final int SPINNER_FIELD_WIDTH = 6;
private static BufferedImage CONFIG_ICON;
private static BufferedImage UNCHECK_ICON;
private static BufferedImage CHECK_ICON;
private static final ImageIcon CONFIG_ICON;
private static final ImageIcon ON_SWITCHER;
private static final ImageIcon OFF_SWITCHER;
private static final ImageIcon SEARCH;
static
{
@@ -92,14 +98,15 @@ public class ConfigPanel extends PluginPanel
{
synchronized (ImageIO.class)
{
CONFIG_ICON = ImageIO.read(ConfigPanel.class.getResourceAsStream("config_icon.png"));
UNCHECK_ICON = ImageIO.read(ConfigPanel.class.getResourceAsStream("disabled.png"));
CHECK_ICON = ImageIO.read(ConfigPanel.class.getResourceAsStream("enabled.png"));
CONFIG_ICON = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("config_edit_icon.png")));
ON_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/on.png")));
OFF_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/off.png")));
SEARCH = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search.png")));
}
}
catch (IOException e)
{
log.warn("Failed to read icon", e);
throw new RuntimeException(e);
}
}
@@ -107,7 +114,7 @@ public class ConfigPanel extends PluginPanel
private final ConfigManager configManager;
private final ScheduledExecutorService executorService;
private final RuneLiteConfig runeLiteConfig;
private final JTextField searchBar = new JTextField();
private final IconTextField searchBar = new IconTextField();
private Map<String, JPanel> children = new TreeMap<>();
private int scrollBarPosition = 0;
@@ -119,6 +126,10 @@ public class ConfigPanel extends PluginPanel
this.executorService = executorService;
this.runeLiteConfig = runeLiteConfig;
searchBar.setIcon(SEARCH);
searchBar.setPreferredSize(new Dimension(100, 30));
searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR);
searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR);
searchBar.getDocument().addDocumentListener(new DocumentListener()
{
@Override
@@ -140,6 +151,10 @@ public class ConfigPanel extends PluginPanel
}
});
setBorder(new EmptyBorder(10, 10, 10, 10));
setLayout(new DynamicGridLayout(0, 1, 0, 5));
setBackground(ColorScheme.DARK_GRAY_COLOR);
rebuildPluginList();
openConfigList();
}
@@ -150,42 +165,54 @@ public class ConfigPanel extends PluginPanel
Map<String, JPanel> newChildren = new TreeMap<>();
pluginManager.getPlugins().stream()
.filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden())
.sorted(Comparator.comparing(left -> left.getClass().getAnnotation(PluginDescriptor.class).name()))
.forEach(plugin ->
{
final Config pluginConfigProxy = pluginManager.getPluginConfigProxy(plugin);
final String pluginName = plugin.getClass().getAnnotation(PluginDescriptor.class).name();
.filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden())
.sorted(Comparator.comparing(left -> left.getClass().getAnnotation(PluginDescriptor.class).name()))
.forEach(plugin ->
{
final Config pluginConfigProxy = pluginManager.getPluginConfigProxy(plugin);
final String pluginName = plugin.getClass().getAnnotation(PluginDescriptor.class).name();
final JPanel groupPanel = buildGroupPanel();
groupPanel.add(new JLabel(pluginName), BorderLayout.CENTER);
final JPanel groupPanel = buildGroupPanel();
final JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(1, 2, 3, 0));
groupPanel.add(buttonPanel, BorderLayout.LINE_END);
JLabel name = new JLabel(pluginName);
name.setForeground(Color.WHITE);
final JButton editConfigButton = buildConfigButton(pluginConfigProxy);
buttonPanel.add(editConfigButton);
groupPanel.add(name, BorderLayout.CENTER);
final JButton toggleButton = buildToggleButton(plugin);
buttonPanel.add(toggleButton);
final JPanel buttonPanel = new JPanel();
buttonPanel.setOpaque(false);
buttonPanel.setLayout(new GridLayout(1, 2));
groupPanel.add(buttonPanel, BorderLayout.LINE_END);
newChildren.put(pluginName, groupPanel);
});
final JLabel editConfigButton = buildConfigButton(pluginConfigProxy);
buttonPanel.add(editConfigButton);
final JLabel toggleButton = buildToggleButton(plugin);
toggleButton.setHorizontalAlignment(SwingConstants.RIGHT);
buttonPanel.add(toggleButton);
newChildren.put(pluginName, groupPanel);
});
final JPanel groupPanel = buildGroupPanel();
groupPanel.add(new JLabel("RuneLite"), BorderLayout.CENTER);
JLabel name = new JLabel("RuneLite");
name.setForeground(Color.WHITE);
groupPanel.add(name, BorderLayout.CENTER);
final JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(1, 2, 3, 0));
buttonPanel.setOpaque(false);
buttonPanel.setLayout(new GridLayout(1, 2));
groupPanel.add(buttonPanel, BorderLayout.LINE_END);
final JButton editConfigButton = buildConfigButton(runeLiteConfig);
final JLabel editConfigButton = buildConfigButton(runeLiteConfig);
buttonPanel.add(editConfigButton);
final JButton toggleButton = buildToggleButton(null);
final JLabel toggleButton = buildToggleButton(null);
toggleButton.setVisible(false);
buttonPanel.add(toggleButton);
newChildren.put("RuneLite", groupPanel);
children = newChildren;
@@ -197,15 +224,17 @@ public class ConfigPanel extends PluginPanel
// Create base panel for the config button and enabled/disabled button
final JPanel groupPanel = new JPanel();
groupPanel.setLayout(new BorderLayout(3, 0));
groupPanel.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH, 20));
groupPanel.setOpaque(false);
return groupPanel;
}
private JButton buildConfigButton(Config config)
private JLabel buildConfigButton(Config config)
{
// Create edit config button and disable it by default
final JButton editConfigButton = new JButton(new ImageIcon(CONFIG_ICON));
editConfigButton.setPreferredSize(new Dimension(32, 0));
editConfigButton.setEnabled(false);
final JLabel editConfigButton = new JLabel(CONFIG_ICON);
editConfigButton.setPreferredSize(new Dimension(25, 0));
editConfigButton.setVisible(false);
// If we have configuration proxy enable the button and add edit config listener
if (config != null)
@@ -215,8 +244,15 @@ public class ConfigPanel extends PluginPanel
if (!configEmpty)
{
editConfigButton.addActionListener(ae -> openGroupConfigPanel(config, configDescriptor, configManager));
editConfigButton.setEnabled(true);
editConfigButton.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent mouseEvent)
{
openGroupConfigPanel(config, configDescriptor, configManager);
}
});
editConfigButton.setVisible(true);
editConfigButton.setToolTipText("Edit plugin configuration");
}
}
@@ -224,11 +260,11 @@ public class ConfigPanel extends PluginPanel
return editConfigButton;
}
private JButton buildToggleButton(Plugin plugin)
private JLabel buildToggleButton(Plugin plugin)
{
// Create enabling/disabling button
final JButton toggleButton = new JButton(new ImageIcon(CHECK_ICON));
toggleButton.setPreferredSize(new Dimension(32, 0));
final JLabel toggleButton = new JLabel(ON_SWITCHER);
toggleButton.setPreferredSize(new Dimension(25, 0));
if (plugin == null)
{
@@ -238,36 +274,43 @@ public class ConfigPanel extends PluginPanel
highlightButton(toggleButton, pluginManager.isPluginEnabled(plugin));
toggleButton.addActionListener(e -> executorService.submit(() ->
toggleButton.addMouseListener(new MouseAdapter()
{
final boolean enabled = pluginManager.isPluginEnabled(plugin);
pluginManager.setPluginEnabled(plugin, !enabled);
try
@Override
public void mousePressed(MouseEvent mouseEvent)
{
if (enabled)
executorService.submit(() ->
{
pluginManager.stopPlugin(plugin);
}
else
{
pluginManager.startPlugin(plugin);
}
}
catch (PluginInstantiationException ex)
{
log.warn("Error during starting/stopping plugin {}", plugin.getClass().getSimpleName(), ex);
}
final boolean enabled = pluginManager.isPluginEnabled(plugin);
pluginManager.setPluginEnabled(plugin, !enabled);
highlightButton(toggleButton, !enabled);
}));
try
{
if (enabled)
{
pluginManager.stopPlugin(plugin);
}
else
{
pluginManager.startPlugin(plugin);
}
}
catch (PluginInstantiationException ex)
{
log.warn("Error during starting/stopping plugin {}", plugin.getClass().getSimpleName(), ex);
}
highlightButton(toggleButton, !enabled);
});
}
});
return toggleButton;
}
private void highlightButton(JButton button, boolean enabled)
private void highlightButton(JLabel button, boolean enabled)
{
button.setIcon(enabled ? new ImageIcon(CHECK_ICON) : new ImageIcon(UNCHECK_ICON));
button.setIcon(enabled ? ON_SWITCHER : OFF_SWITCHER);
button.setToolTipText(enabled ? "Disable plugin" : "Enable plugin");
}
@@ -301,7 +344,11 @@ public class ConfigPanel extends PluginPanel
private void openConfigList()
{
removeAll();
add(new JLabel("Plugin Configuration", SwingConstants.CENTER));
JLabel title = new JLabel("Configuration", SwingConstants.LEFT);
title.setForeground(Color.WHITE);
add(title);
add(searchBar);
onSearchBarChanged();
@@ -331,6 +378,7 @@ public class ConfigPanel extends PluginPanel
if (component instanceof JCheckBox)
{
JCheckBox checkbox = (JCheckBox) component;
checkbox.setOpaque(false);
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), "" + checkbox.isSelected());
}
@@ -355,6 +403,8 @@ public class ConfigPanel extends PluginPanel
if (component instanceof JComboBox)
{
JComboBox jComboBox = (JComboBox) component;
jComboBox.setRenderer(new ComboBoxListRenderer());
jComboBox.setForeground(Color.WHITE);
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), ((Enum) jComboBox.getSelectedItem()).name());
}
}
@@ -376,6 +426,7 @@ public class ConfigPanel extends PluginPanel
}
JPanel item = new JPanel();
item.setOpaque(false);
item.setLayout(new BorderLayout());
name = cid.getItem().name();
JLabel configEntryName = new JLabel(name);
@@ -385,6 +436,8 @@ public class ConfigPanel extends PluginPanel
if (cid.getType() == boolean.class)
{
JCheckBox checkbox = new JCheckBox();
checkbox.setOpaque(false);
checkbox.setBackground(ColorScheme.LIGHT_GRAY_COLOR);
checkbox.setSelected(Boolean.parseBoolean(configManager.getConfiguration(cd.getGroup().keyName(), cid.getItem().keyName())));
checkbox.addActionListener(ae -> changeConfiguration(config, checkbox, cd, cid));
@@ -462,6 +515,7 @@ public class ConfigPanel extends PluginPanel
if (cid.getType() == Dimension.class)
{
JPanel dimensionPanel = new JPanel();
dimensionPanel.setOpaque(false);
dimensionPanel.setLayout(new BorderLayout());
String str = configManager.getConfiguration(cd.getGroup().keyName(), cid.getItem().keyName());
@@ -482,7 +536,7 @@ public class ConfigPanel extends PluginPanel
heightSpinnerTextField.setColumns(4);
ChangeListener listener = e ->
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), widthSpinner.getValue() + "x" + heightSpinner.getValue());
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), widthSpinner.getValue() + "x" + heightSpinner.getValue());
widthSpinner.addChangeListener(listener);
heightSpinner.addChangeListener(listener);
@@ -498,6 +552,9 @@ public class ConfigPanel extends PluginPanel
{
Class<? extends Enum> type = (Class<? extends Enum>) cid.getType();
JComboBox box = new JComboBox(type.getEnumConstants());
box.setPreferredSize(new Dimension(box.getPreferredSize().width, 25));
box.setRenderer(new ComboBoxListRenderer());
box.setForeground(Color.WHITE);
box.setFocusable(false);
box.setPrototypeDisplayValue("XXXXXXXX"); //sorry but this is the way to keep the size of the combobox in check.
try
@@ -541,4 +598,4 @@ public class ConfigPanel extends PluginPanel
revalidate();
getScrollPane().getVerticalScrollBar().setValue(0);
}
}
}

View File

@@ -78,6 +78,7 @@ public class ConfigPlugin extends Plugin
navButton = NavigationButton.builder()
.name("Configuration")
.icon(icon)
.priority(0)
.panel(configPanel)
.build();

View File

@@ -32,6 +32,7 @@ import javax.swing.JButton;
import javax.swing.JPanel;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
@Slf4j
@@ -52,6 +53,8 @@ public class DevToolsPanel extends PluginPanel
this.plugin = plugin;
this.widgetInspector = widgetInspector;
setBackground(ColorScheme.DARK_GRAY_COLOR);
varTracker = new VarTracker(client);
add(createOptionsPanel());
}
@@ -59,6 +62,7 @@ public class DevToolsPanel extends PluginPanel
private JPanel createOptionsPanel()
{
final JPanel container = new JPanel();
container.setBackground(ColorScheme.DARK_GRAY_COLOR);
container.setLayout(new GridLayout(0, 2, 3, 3));
final JButton renderPlayersBtn = new JButton("Players");

View File

@@ -122,6 +122,7 @@ public class DevToolsPlugin extends Plugin
navButton = NavigationButton.builder()
.name("Developer Tools")
.icon(icon)
.priority(1)
.panel(panel)
.build();

View File

@@ -27,15 +27,16 @@ package net.runelite.client.plugins.farmingtracker;
import java.awt.Color;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.client.ui.ColorScheme;
@RequiredArgsConstructor
@Getter
public enum CropState
{
HARVESTABLE(Color.GREEN),
GROWING(Color.GREEN),
DISEASED(Color.ORANGE),
DEAD(Color.RED);
HARVESTABLE(ColorScheme.PROGRESS_COMPLETE_COLOR),
GROWING(ColorScheme.PROGRESS_COMPLETE_COLOR),
DISEASED(ColorScheme.PROGRESS_INPROGRESS_COLOR),
DEAD(ColorScheme.PROGRESS_ERROR_COLOR);
private final Color color;
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 Abex
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,13 +25,19 @@
*/
package net.runelite.client.plugins.farmingtracker;
import com.google.common.base.Strings;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.GroupLayout;
import java.awt.GridLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.border.EmptyBorder;
import lombok.Getter;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.components.ThinProgressBar;
import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
@Getter
class FarmingPatchPanel extends JPanel
@@ -38,42 +45,43 @@ class FarmingPatchPanel extends JPanel
private final FarmingPatch patch;
private final JLabel icon = new JLabel();
private final JLabel estimate = new JLabel();
private final JProgressBar progress = new JProgressBar();
private final ThinProgressBar progress = new ThinProgressBar();
FarmingPatchPanel(FarmingPatch patch)
{
this.patch = patch;
GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
setLayout(new BorderLayout());
setOpaque(false);
setBorder(new EmptyBorder(7, 0, 0, 0));
JPanel topContainer = new JPanel();
topContainer.setBorder(new EmptyBorder(7, 7, 6, 0));
topContainer.setLayout(new BorderLayout());
topContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
final JLabel location = new JLabel(patch.getRegion().getName() + " " + patch.getName());
location.setFont(FontManager.getRunescapeSmallFont());
icon.setMinimumSize(new Dimension(36, 32));
layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
.addComponent(icon)
.addGroup(layout.createSequentialGroup()
.addGap(1)
.addComponent(location)
.addGap(1)
.addComponent(estimate)
)
)
.addComponent(progress, 8, 8, 8)
.addGap(4)
);
JPanel infoPanel = new JPanel();
infoPanel.setOpaque(false);
infoPanel.setLayout(new GridLayout(2, 1));
infoPanel.setBorder(new EmptyBorder(4, 4, 4, 0));
layout.setHorizontalGroup(layout.createParallelGroup()
.addGroup(layout.createSequentialGroup()
.addComponent(icon)
.addGroup(layout.createParallelGroup()
.addComponent(location)
.addComponent(estimate)
)
)
.addComponent(progress)
);
final JLabel location = new JShadowedLabel(patch.getRegion().getName()
+ (Strings.isNullOrEmpty(patch.getName()) ? "" : " (" + patch.getName() + ")"));
location.setFont(FontManager.getRunescapeSmallFont());
location.setForeground(Color.WHITE);
estimate.setFont(FontManager.getRunescapeSmallFont());
estimate.setForeground(Color.GRAY);
infoPanel.add(location);
infoPanel.add(estimate);
topContainer.add(icon, BorderLayout.WEST);
topContainer.add(infoPanel, BorderLayout.CENTER);
add(topContainer, BorderLayout.NORTH);
add(progress, BorderLayout.SOUTH);
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 Abex
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,11 +25,13 @@
*/
package net.runelite.client.plugins.farmingtracker;
import com.google.common.base.Strings;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
@@ -37,9 +40,10 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
@@ -47,7 +51,11 @@ import net.runelite.api.vars.Autoweed;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.AsyncBufferedImage;
import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.materialtabs.MaterialTab;
import net.runelite.client.ui.components.materialtabs.MaterialTabGroup;
@Slf4j
class FarmingTrackerPanel extends PluginPanel
@@ -61,6 +69,10 @@ class FarmingTrackerPanel extends PluginPanel
private List<FarmingPatchPanel> patchPanels = new ArrayList<>();
/* This is the panel the tabs' respective panels will be displayed on. */
private final JPanel display = new JPanel();
private final MaterialTabGroup tabGroup = new MaterialTabGroup(display);
FarmingTrackerPanel(
Client client,
ItemManager itemManager,
@@ -77,11 +89,19 @@ class FarmingTrackerPanel extends PluginPanel
this.config = config;
setLayout(new BorderLayout());
setBackground(ColorScheme.DARK_GRAY_COLOR);
display.setOpaque(false);
display.setBorder(new EmptyBorder(10, 10, 8, 10));
tabGroup.setBorder(new EmptyBorder(10, 1, 0, 0));
add(tabGroup, BorderLayout.NORTH);
add(display, BorderLayout.CENTER);
JTabbedPane tabs = new JTabbedPane();
farmingWorld.getTabs().forEach((tab, patches) ->
{
JPanel panel = new JPanel(new GridBagLayout())
JPanel container = new JPanel(new GridBagLayout())
{
@Override
public Dimension getPreferredSize()
@@ -89,7 +109,7 @@ class FarmingTrackerPanel extends PluginPanel
return new Dimension(PluginPanel.PANEL_WIDTH, super.getPreferredSize().height);
}
};
panel.setBorder(new EmptyBorder(2, 6, 6, 6));
container.setBackground(ColorScheme.DARK_GRAY_COLOR);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
@@ -97,43 +117,82 @@ class FarmingTrackerPanel extends PluginPanel
c.gridx = 0;
c.gridy = 0;
PatchImplementation lastImpl = null;
boolean first = true;
for (FarmingPatch patch : patches)
{
FarmingPatchPanel p = new FarmingPatchPanel(patch);
/* Show labels to subdivide tabs into sections */
if (patch.getImplementation() != lastImpl && !Strings.isNullOrEmpty(patch.getImplementation().getName()))
{
JLabel groupLabel = new JLabel(patch.getImplementation().getName());
if (first)
{
first = false;
groupLabel.setBorder(new EmptyBorder(4, 0, 0, 0));
}
else
{
groupLabel.setBorder(new EmptyBorder(15, 0, 0, 0));
}
groupLabel.setFont(FontManager.getRunescapeSmallFont());
container.add(groupLabel, c);
c.gridy++;
lastImpl = patch.getImplementation();
}
patchPanels.add(p);
panel.add(p, c);
container.add(p, c);
c.gridy++;
/* This is a weird hack to remove the top border on the first tracker of every tab */
if (first)
{
first = false;
p.setBorder(null);
}
}
JPanel wrapped = new JPanel(new BorderLayout());
wrapped.add(panel, BorderLayout.NORTH);
wrapped.add(container, BorderLayout.NORTH);
wrapped.setBackground(ColorScheme.DARK_GRAY_COLOR);
JScrollPane scroller = new JScrollPane(wrapped);
scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroller.getVerticalScrollBar().setUnitIncrement(16);
scroller.getVerticalScrollBar().setPreferredSize(new Dimension(16, 0));
scroller.getVerticalScrollBar().setBorder(new EmptyBorder(0, 9, 0, 0));
scroller.setBackground(ColorScheme.DARK_GRAY_COLOR);
MaterialTab materialTab = new MaterialTab("", tabGroup, scroller);
materialTab.setName(tab.getName());
AsyncBufferedImage icon = itemManager.getImage(tab.getItemID());
tabs.addTab(null, null, scroller, tab.getName());
int idx = tabs.getTabCount() - 1;
Runnable resize = () ->
{
tabs.setIconAt(idx, new ImageIcon(icon.getScaledInstance(24, 21, Image.SCALE_SMOOTH)));
BufferedImage subIcon = icon.getSubimage(0, 0, 32, 32);
materialTab.setIcon(new ImageIcon(subIcon.getScaledInstance(24, 24, Image.SCALE_SMOOTH)));
materialTab.setHorizontalAlignment(SwingConstants.CENTER);
materialTab.setVerticalAlignment(SwingConstants.CENTER);
materialTab.setOpaque(true);
materialTab.setBackground(ColorScheme.DARKER_GRAY_COLOR);
materialTab.setPreferredSize(new Dimension(30, 27));
};
icon.onChanged(resize);
resize.run();
materialTab.setOnSelectEvent(() -> config.setPatch(tab));
tabGroup.addTab(materialTab);
if (config.patch() == tab)
{
tabs.setSelectedComponent(scroller);
tabGroup.select(materialTab);
}
tabs.addChangeListener(e ->
{
if (tabs.getSelectedComponent() == scroller)
{
config.setPatch(tab);
}
});
});
add(tabs, BorderLayout.CENTER);
}
void update()
@@ -178,10 +237,11 @@ class FarmingTrackerPanel extends PluginPanel
PatchState state = unixTime <= 0 ? null : patch.getImplementation().forVarbitValue(value);
if (state == null)
{
panel.getIcon().setIcon(null);
itemManager.getImage(Produce.WEEDS.getItemID()).addTo(panel.getIcon());
panel.getIcon().setToolTipText("Unknown state");
panel.getProgress().setMaximum(0);
panel.getProgress().setMaximumValue(0);
panel.getProgress().setValue(0);
panel.getProgress().setVisible(false);
panel.getEstimate().setText("Unknown");
panel.getProgress().setBackground(null);
}
@@ -298,9 +358,20 @@ class FarmingTrackerPanel extends PluginPanel
}
}
panel.getProgress().setBackground(state.getCropState().getColor().darker());
panel.getProgress().setMaximum(stages - 1);
panel.getProgress().setValue(stage);
/* Hide any fully grown weeds' progress bar. */
if (state.getProduce() != Produce.WEEDS
|| (state.getProduce() == Produce.WEEDS && !autoweed && stage < stages - 1))
{
panel.getProgress().setVisible(true);
panel.getProgress().setForeground(state.getCropState().getColor().darker());
panel.getProgress().setMaximumValue(stages - 1);
panel.getProgress().setValue(stage);
panel.getProgress().update();
}
else
{
panel.getProgress().setVisible(false);
}
}
}
}

View File

@@ -98,6 +98,7 @@ public class FarmingTrackerPlugin extends Plugin
.name("Farming Tracker")
.icon(icon)
.panel(panel)
.priority(4)
.build();
pluginToolbar.addNavigation(navButton);

View File

@@ -60,24 +60,24 @@ public class FarmingWorld
));
add(new FarmingRegion("Ardougne", 10290,
new FarmingPatch("Bush", Varbits.FARMING_4771, PatchImplementation.BUSH)
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH)
));
add(new FarmingRegion("Ardougne", 10548,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB)
));
add(new FarmingRegion("Brimhaven", 11058,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE),
new FarmingPatch("Spirit Tree", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE)
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE)
));
add(new FarmingRegion("Catherby", 11062,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB)
));
add(new FarmingRegion("Catherby", 11317,
@@ -85,20 +85,20 @@ public class FarmingWorld
));
add(new FarmingRegion("Champion's Guild", 12596,
new FarmingPatch("Bush", Varbits.FARMING_4771, PatchImplementation.BUSH)
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH)
));
add(new FarmingRegion("Draynor Manor", 12340,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BELLADONNA)
new FarmingPatch("Belladonna", Varbits.FARMING_4771, PatchImplementation.BELLADONNA)
));
add(new FarmingRegion("Entrana", 11060,
new FarmingPatch("Hops", Varbits.FARMING_4771, PatchImplementation.HOPS)
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS)
));
add(new FarmingRegion("Etceteria", 10300,
new FarmingPatch("Bush", Varbits.FARMING_4771, PatchImplementation.BUSH),
new FarmingPatch("Spirit Tree", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE)
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH),
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE)
));
add(new FarmingRegion("Falador", 11828,
@@ -107,7 +107,7 @@ public class FarmingWorld
add(new FarmingRegion("Falador", 12083,
new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB)
)
{
@@ -141,11 +141,11 @@ public class FarmingWorld
add(new FarmingRegion("Kourend", 7222,
new FarmingPatch("North East", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South West", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB)
));
add(new FarmingRegion("Kourend", 6711,
new FarmingPatch("Spirit Tree", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE)
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE)
));
add(new FarmingRegion("Kourend", 7223,
new FarmingPatch("West 1", Varbits.GRAPES_4953, PatchImplementation.GRAPES),
@@ -167,7 +167,7 @@ public class FarmingWorld
));
add(new FarmingRegion("Lumbridge", 12851,
new FarmingPatch("Hops", Varbits.FARMING_4771, PatchImplementation.HOPS)
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS)
));
add(new FarmingRegion("Lumbridge", 12594,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE)
@@ -179,17 +179,17 @@ public class FarmingWorld
add(new FarmingRegion("Morytania", 14391,
new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB)
));
add(new FarmingRegion("Port Sarim", 12082,
new FarmingPatch("Spirit tree", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE)
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE)
));
add(new FarmingRegion("Rimmington", 11570,
new FarmingPatch("Bush", Varbits.FARMING_4771, PatchImplementation.BUSH)
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH)
), 11826);
add(new FarmingRegion("Seers' Village", 10551,

View File

@@ -31,7 +31,7 @@ import lombok.RequiredArgsConstructor;
@Getter
public enum PatchImplementation
{
CACTUS(Tab.SPECIAL)
CACTUS(Tab.SPECIAL, "")
{
@Override
PatchState forVarbitValue(int value)
@@ -84,7 +84,7 @@ public enum PatchImplementation
return null;
}
},
BELLADONNA(Tab.SPECIAL)
BELLADONNA(Tab.SPECIAL, "")
{
@Override
PatchState forVarbitValue(int value)
@@ -122,7 +122,7 @@ public enum PatchImplementation
return null;
}
},
MUSHROOM(Tab.SPECIAL)
MUSHROOM(Tab.SPECIAL, "")
{
@Override
PatchState forVarbitValue(int value)
@@ -160,7 +160,7 @@ public enum PatchImplementation
return null;
}
},
ALLOTMENT(Tab.ALLOTMENT)
ALLOTMENT(Tab.ALLOTMENT, "")
{
@Override
PatchState forVarbitValue(int value)
@@ -473,240 +473,7 @@ public enum PatchImplementation
return null;
}
},
FLOWER(Tab.SPECIAL)
{
@Override
PatchState forVarbitValue(int value)
{
if (value >= 0 && value <= 3)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7842,7841,7840
return new PatchState(Produce.WEEDS, CropState.GROWING, 3 - value);
}
if (value >= 4 && value <= 7)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 8 && value <= 11)
{
// Marigold[,Inspect,,Guide,] 7867,7868,7869,7870
return new PatchState(Produce.MARIGOLD, CropState.GROWING, value - 8);
}
if (value == 12)
{
// Marigold[Pick,Inspect,,Guide,] 7871
return new PatchState(Produce.MARIGOLD, CropState.HARVESTABLE, 12 - value);
}
if (value >= 13 && value <= 16)
{
// Rosemary[,Inspect,,Guide,] 7899,7900,7901,7902
return new PatchState(Produce.ROSEMARY, CropState.GROWING, value - 13);
}
if (value == 17)
{
// Rosemary[Pick,Inspect,,Guide,] 7903
return new PatchState(Produce.ROSEMARY, CropState.HARVESTABLE, 17 - value);
}
if (value >= 18 && value <= 21)
{
// Nasturtium[,Inspect,,Guide,] 7883,7884,7885,7886
return new PatchState(Produce.NASTURTIUM, CropState.GROWING, value - 18);
}
if (value == 22)
{
// Nasturtium[Pick,Inspect,,Guide,] 7887
return new PatchState(Produce.NASTURTIUM, CropState.HARVESTABLE, 22 - value);
}
if (value >= 23 && value <= 26)
{
// Woad[,Inspect,,Guide,] 7919,7920,7921,7922
return new PatchState(Produce.WOAD, CropState.GROWING, value - 23);
}
if (value == 27)
{
// Woad[Pick,Inspect,,Guide,] 7923
return new PatchState(Produce.WOAD, CropState.HARVESTABLE, 27 - value);
}
if (value >= 28 && value <= 31)
{
// Limpwurt[,Inspect,,Guide,] 7851,7852,7853,7854
return new PatchState(Produce.LIMPWURT, CropState.GROWING, value - 28);
}
if (value == 32)
{
// Limpwurt[Pick,Inspect,,Guide,] 7855
return new PatchState(Produce.LIMPWURT, CropState.HARVESTABLE, 32 - value);
}
if (value >= 33 && value <= 35)
{
// Scarecrow[Rake,Inspect,,Guide,Remove] 7918,7917,7916
return new PatchState(Produce.SCARECROW, CropState.GROWING, 35 - value);
}
if (value == 36)
{
// Scarecrow[Remove,Inspect,,Guide,] 7915
return new PatchState(Produce.SCARECROW, CropState.GROWING, 0);
}
if (value >= 37 && value <= 71)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 72 && value <= 75)
{
// Marigold[,Inspect,,Guide,] 7872,7873,7874,7875
return new PatchState(Produce.MARIGOLD, CropState.GROWING, value - 72);
}
if (value == 76)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 77 && value <= 80)
{
// Rosemary[,Inspect,,Guide,] 7904,7905,7906,7907
return new PatchState(Produce.ROSEMARY, CropState.GROWING, value - 77);
}
if (value == 81)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 82 && value <= 85)
{
// Nasturtium[,Inspect,,Guide,] 7888,7889,7890,7891
return new PatchState(Produce.NASTURTIUM, CropState.GROWING, value - 82);
}
if (value == 86)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 87 && value <= 90)
{
// Woad[,Inspect,,Guide,] 7924,7925,7926,7927
return new PatchState(Produce.WOAD, CropState.GROWING, value - 87);
}
if (value == 91)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 92 && value <= 95)
{
// Limpwurt[,Inspect,,Guide,] 7856,7857,7858,7859
return new PatchState(Produce.LIMPWURT, CropState.GROWING, value - 92);
}
if (value >= 96 && value <= 136)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 137 && value <= 139)
{
// Diseased marigold[Cure,Inspect,,Guide,] 7876,7877,7878
return new PatchState(Produce.MARIGOLD, CropState.DISEASED, value - 136);
}
if (value >= 140 && value <= 141)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 142 && value <= 144)
{
// Diseased rosemary[Cure,Inspect,,Guide,] 7908,7909,7910
return new PatchState(Produce.ROSEMARY, CropState.DISEASED, value - 141);
}
if (value >= 145 && value <= 146)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 147 && value <= 149)
{
// Diseased nasturtium[Cure,Inspect,,Guide,] 7892,7893,7894
return new PatchState(Produce.NASTURTIUM, CropState.DISEASED, value - 146);
}
if (value >= 150 && value <= 151)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 152 && value <= 154)
{
// Diseased woad[Cure,Inspect,,Guide,] 7928,7929,7930
return new PatchState(Produce.WOAD, CropState.DISEASED, value - 151);
}
if (value >= 155 && value <= 156)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 157 && value <= 159)
{
// Diseased limpwurt[Cure,Inspect,,Guide,] 7860,7861,7862
return new PatchState(Produce.LIMPWURT, CropState.DISEASED, value - 156);
}
if (value >= 160 && value <= 200)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 201 && value <= 203)
{
// Dead marigold[Clear,Inspect,,Guide,] 7879,7880,7881,7882
return new PatchState(Produce.MARIGOLD, CropState.DEAD, value - 200);
}
if (value == 205)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 206 && value <= 208)
{
// Dead rosemary[Clear,Inspect,,Guide,] 7911,7912,7913,7914
return new PatchState(Produce.ROSEMARY, CropState.DEAD, value - 205);
}
if (value == 210)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 211 && value <= 213)
{
// Dead nasturtium[Clear,Inspect,,Guide,] 7895,7896,7897,7898
return new PatchState(Produce.NASTURTIUM, CropState.DEAD, value - 210);
}
if (value == 215)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 216 && value <= 218)
{
// Dead woad[Clear,Inspect,,Guide,] 7931,7932,7933,7934
return new PatchState(Produce.WOAD, CropState.DEAD, value - 215);
}
if (value == 220)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 221 && value <= 224)
{
// Dead limpwurt[Clear,Inspect,,Guide,] 7863,7864,7865,7866
return new PatchState(Produce.LIMPWURT, CropState.DEAD, value - 220);
}
if (value >= 225 && value <= 255)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
return null;
}
},
HERB(Tab.HERB)
HERB(Tab.HERB, "")
{
@Override
PatchState forVarbitValue(int value)
@@ -970,7 +737,240 @@ public enum PatchImplementation
return null;
}
},
BUSH(Tab.SPECIAL)
FLOWER(Tab.HERB, "Flowers")
{
@Override
PatchState forVarbitValue(int value)
{
if (value >= 0 && value <= 3)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7842,7841,7840
return new PatchState(Produce.WEEDS, CropState.GROWING, 3 - value);
}
if (value >= 4 && value <= 7)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 8 && value <= 11)
{
// Marigold[,Inspect,,Guide,] 7867,7868,7869,7870
return new PatchState(Produce.MARIGOLD, CropState.GROWING, value - 8);
}
if (value == 12)
{
// Marigold[Pick,Inspect,,Guide,] 7871
return new PatchState(Produce.MARIGOLD, CropState.HARVESTABLE, 12 - value);
}
if (value >= 13 && value <= 16)
{
// Rosemary[,Inspect,,Guide,] 7899,7900,7901,7902
return new PatchState(Produce.ROSEMARY, CropState.GROWING, value - 13);
}
if (value == 17)
{
// Rosemary[Pick,Inspect,,Guide,] 7903
return new PatchState(Produce.ROSEMARY, CropState.HARVESTABLE, 17 - value);
}
if (value >= 18 && value <= 21)
{
// Nasturtium[,Inspect,,Guide,] 7883,7884,7885,7886
return new PatchState(Produce.NASTURTIUM, CropState.GROWING, value - 18);
}
if (value == 22)
{
// Nasturtium[Pick,Inspect,,Guide,] 7887
return new PatchState(Produce.NASTURTIUM, CropState.HARVESTABLE, 22 - value);
}
if (value >= 23 && value <= 26)
{
// Woad[,Inspect,,Guide,] 7919,7920,7921,7922
return new PatchState(Produce.WOAD, CropState.GROWING, value - 23);
}
if (value == 27)
{
// Woad[Pick,Inspect,,Guide,] 7923
return new PatchState(Produce.WOAD, CropState.HARVESTABLE, 27 - value);
}
if (value >= 28 && value <= 31)
{
// Limpwurt[,Inspect,,Guide,] 7851,7852,7853,7854
return new PatchState(Produce.LIMPWURT, CropState.GROWING, value - 28);
}
if (value == 32)
{
// Limpwurt[Pick,Inspect,,Guide,] 7855
return new PatchState(Produce.LIMPWURT, CropState.HARVESTABLE, 32 - value);
}
if (value >= 33 && value <= 35)
{
// Scarecrow[Rake,Inspect,,Guide,Remove] 7918,7917,7916
return new PatchState(Produce.SCARECROW, CropState.GROWING, 35 - value);
}
if (value == 36)
{
// Scarecrow[Remove,Inspect,,Guide,] 7915
return new PatchState(Produce.SCARECROW, CropState.GROWING, 0);
}
if (value >= 37 && value <= 71)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 72 && value <= 75)
{
// Marigold[,Inspect,,Guide,] 7872,7873,7874,7875
return new PatchState(Produce.MARIGOLD, CropState.GROWING, value - 72);
}
if (value == 76)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 77 && value <= 80)
{
// Rosemary[,Inspect,,Guide,] 7904,7905,7906,7907
return new PatchState(Produce.ROSEMARY, CropState.GROWING, value - 77);
}
if (value == 81)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 82 && value <= 85)
{
// Nasturtium[,Inspect,,Guide,] 7888,7889,7890,7891
return new PatchState(Produce.NASTURTIUM, CropState.GROWING, value - 82);
}
if (value == 86)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 87 && value <= 90)
{
// Woad[,Inspect,,Guide,] 7924,7925,7926,7927
return new PatchState(Produce.WOAD, CropState.GROWING, value - 87);
}
if (value == 91)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 92 && value <= 95)
{
// Limpwurt[,Inspect,,Guide,] 7856,7857,7858,7859
return new PatchState(Produce.LIMPWURT, CropState.GROWING, value - 92);
}
if (value >= 96 && value <= 136)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 137 && value <= 139)
{
// Diseased marigold[Cure,Inspect,,Guide,] 7876,7877,7878
return new PatchState(Produce.MARIGOLD, CropState.DISEASED, value - 136);
}
if (value >= 140 && value <= 141)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 142 && value <= 144)
{
// Diseased rosemary[Cure,Inspect,,Guide,] 7908,7909,7910
return new PatchState(Produce.ROSEMARY, CropState.DISEASED, value - 141);
}
if (value >= 145 && value <= 146)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 147 && value <= 149)
{
// Diseased nasturtium[Cure,Inspect,,Guide,] 7892,7893,7894
return new PatchState(Produce.NASTURTIUM, CropState.DISEASED, value - 146);
}
if (value >= 150 && value <= 151)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 152 && value <= 154)
{
// Diseased woad[Cure,Inspect,,Guide,] 7928,7929,7930
return new PatchState(Produce.WOAD, CropState.DISEASED, value - 151);
}
if (value >= 155 && value <= 156)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 157 && value <= 159)
{
// Diseased limpwurt[Cure,Inspect,,Guide,] 7860,7861,7862
return new PatchState(Produce.LIMPWURT, CropState.DISEASED, value - 156);
}
if (value >= 160 && value <= 200)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 201 && value <= 203)
{
// Dead marigold[Clear,Inspect,,Guide,] 7879,7880,7881,7882
return new PatchState(Produce.MARIGOLD, CropState.DEAD, value - 200);
}
if (value == 205)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 206 && value <= 208)
{
// Dead rosemary[Clear,Inspect,,Guide,] 7911,7912,7913,7914
return new PatchState(Produce.ROSEMARY, CropState.DEAD, value - 205);
}
if (value == 210)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 211 && value <= 213)
{
// Dead nasturtium[Clear,Inspect,,Guide,] 7895,7896,7897,7898
return new PatchState(Produce.NASTURTIUM, CropState.DEAD, value - 210);
}
if (value == 215)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 216 && value <= 218)
{
// Dead woad[Clear,Inspect,,Guide,] 7931,7932,7933,7934
return new PatchState(Produce.WOAD, CropState.DEAD, value - 215);
}
if (value == 220)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
if (value >= 221 && value <= 224)
{
// Dead limpwurt[Clear,Inspect,,Guide,] 7863,7864,7865,7866
return new PatchState(Produce.LIMPWURT, CropState.DEAD, value - 220);
}
if (value >= 225 && value <= 255)
{
// Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843
return new PatchState(Produce.WEEDS, CropState.GROWING, 3);
}
return null;
}
},
BUSH(Tab.BUSH, "")
{
@Override
PatchState forVarbitValue(int value)
@@ -1209,7 +1209,7 @@ public enum PatchImplementation
return null;
}
},
FRUIT_TREE(Tab.FRUIT_TREE)
FRUIT_TREE(Tab.FRUIT_TREE, "Fruit trees")
{
@Override
PatchState forVarbitValue(int value)
@@ -1458,7 +1458,7 @@ public enum PatchImplementation
return null;
}
},
HOPS(Tab.SPECIAL)
HOPS(Tab.SPECIAL, "Hops")
{
@Override
PatchState forVarbitValue(int value)
@@ -1761,7 +1761,7 @@ public enum PatchImplementation
return null;
}
},
TREE(Tab.TREE)
TREE(Tab.TREE, "")
{
@Override
PatchState forVarbitValue(int value)
@@ -2049,7 +2049,7 @@ public enum PatchImplementation
return null;
}
},
HARDWOOD_TREE(Tab.TREE)
HARDWOOD_TREE(Tab.TREE, "Hardwood Trees")
{
@Override
PatchState forVarbitValue(int value)
@@ -2137,7 +2137,7 @@ public enum PatchImplementation
return null;
}
},
SPIRIT_TREE(Tab.TREE)
SPIRIT_TREE(Tab.TREE, "Spirit Trees")
{
@Override
PatchState forVarbitValue(int value)
@@ -2185,7 +2185,7 @@ public enum PatchImplementation
return null;
}
},
SEAWEED(Tab.SPECIAL)
SEAWEED(Tab.SPECIAL, "Seaweed")
{
@Override
PatchState forVarbitValue(int value)
@@ -2223,7 +2223,7 @@ public enum PatchImplementation
return null;
}
},
CALQUAT(Tab.FRUIT_TREE)
CALQUAT(Tab.FRUIT_TREE, "Calquat")
{
@Override
PatchState forVarbitValue(int value)
@@ -2271,7 +2271,7 @@ public enum PatchImplementation
return null;
}
},
GRAPES(Tab.SPECIAL)
GRAPES(Tab.BUSH, "Grapes")
{
@Override
PatchState forVarbitValue(int value)
@@ -2300,4 +2300,6 @@ public enum PatchImplementation
abstract PatchState forVarbitValue(int value);
private final Tab tab;
private final String name;
}

View File

@@ -36,6 +36,7 @@ public enum Tab
HERB("Herbs", ItemID.GRIMY_RANARR_WEED),
TREE("Trees", ItemID.MAHOGANY_LOGS),
FRUIT_TREE("Fruit Trees", ItemID.PINEAPPLE),
BUSH("Bushes", ItemID.REDBERRIES),
SPECIAL("Special", ItemID.MUSHROOM);
private final String name;

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,6 +29,7 @@ import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.font.FontRenderContext;
@@ -43,13 +45,16 @@ import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.util.LinkBrowser;
import net.runelite.client.util.SwingUtil;
import net.runelite.http.api.RuneLiteAPI;
import net.runelite.http.api.feed.FeedItem;
import net.runelite.http.api.feed.FeedItemType;
@@ -63,8 +68,10 @@ import okhttp3.ResponseBody;
@Slf4j
class FeedPanel extends PluginPanel
{
private static BufferedImage RUNELITE_ICON;
private static BufferedImage OSRS_ICON;
private static final ImageIcon RUNELITE_ICON;
private static final ImageIcon OSRS_ICON;
private static final ImageIcon RESET_ICON;
private static final ImageIcon RESET_ICON_CLICK; //used as a click effect (darker version of the reset icon)
private static final Color TWEET_BACKGROUND = new Color(15, 15, 15);
private static final Color OSRS_NEWS_BACKGROUND = new Color(36, 30, 19);
@@ -74,6 +81,11 @@ class FeedPanel extends PluginPanel
private static final int CONTENT_WIDTH = 148;
private static final int TIME_WIDTH = 20;
/**
* Holds all feed items.
*/
private final JPanel feedContainer = new JPanel();
private static final Comparator<FeedItem> FEED_ITEM_COMPARATOR = (o1, o2) ->
{
if (o1.getType() != o2.getType())
@@ -97,24 +109,16 @@ class FeedPanel extends PluginPanel
{
synchronized (ImageIO.class)
{
RUNELITE_ICON = ImageIO.read(FeedPanel.class.getResourceAsStream("runelite.png"));
BufferedImage reset = ImageIO.read(FeedPanel.class.getResourceAsStream("reset.png"));
RUNELITE_ICON = new ImageIcon(ImageIO.read(FeedPanel.class.getResourceAsStream("runelite.png")));
OSRS_ICON = new ImageIcon(ImageIO.read(FeedPanel.class.getResourceAsStream("osrs.png")));
RESET_ICON = new ImageIcon(reset);
RESET_ICON_CLICK = new ImageIcon(SwingUtil.grayscaleOffset(reset, -100));
}
}
catch (IOException e)
{
log.warn("Client icon failed to load", e);
}
try
{
synchronized (ImageIO.class)
{
OSRS_ICON = ImageIO.read(FeedPanel.class.getResourceAsStream("osrs.png"));
}
}
catch (IOException e)
{
log.warn("OSRS icon failed to load", e);
throw new RuntimeException(e);
}
}
@@ -125,6 +129,56 @@ class FeedPanel extends PluginPanel
{
this.config = config;
this.feedSupplier = feedSupplier;
setBorder(new EmptyBorder(10, 10, 10, 10));
setBackground(ColorScheme.DARK_GRAY_COLOR);
setLayout(new BorderLayout());
feedContainer.setLayout(new GridLayout(0, 1, 0, 4));
feedContainer.setOpaque(false);
/**
* This header contains the "News Feed" title and a refresh icon button.
*/
JPanel header = new JPanel();
header.setOpaque(false);
header.setLayout(new BorderLayout());
header.setBorder(new EmptyBorder(0, 0, 9, 0));
/**
* A refresh icon button, when clicked, it will swap icons for feedback effect and then call
* the rebuildFeed method.
*/
JLabel reset = new JLabel();
reset.setIcon(RESET_ICON);
reset.setVerticalAlignment(SwingConstants.CENTER);
reset.setHorizontalAlignment(SwingConstants.CENTER);
reset.setToolTipText("Refresh");
reset.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent mouseEvent)
{
reset.setIcon(RESET_ICON_CLICK);
rebuildFeed();
}
@Override
public void mouseReleased(MouseEvent mouseEvent)
{
reset.setIcon(RESET_ICON);
}
});
JLabel title = new JLabel("News feed");
title.setForeground(Color.WHITE);
header.add(title, BorderLayout.WEST);
header.add(reset, BorderLayout.EAST);
add(header, BorderLayout.NORTH);
add(feedContainer, BorderLayout.CENTER);
}
void rebuildFeed()
@@ -138,7 +192,7 @@ class FeedPanel extends PluginPanel
SwingUtilities.invokeLater(() ->
{
removeAll();
feedContainer.removeAll();
feed.getItems()
.stream()
@@ -207,14 +261,14 @@ class FeedPanel extends PluginPanel
case OSRS_NEWS:
if (OSRS_ICON != null)
{
avatar.setIcon(new ImageIcon(OSRS_ICON));
avatar.setIcon(OSRS_ICON);
}
avatarAndRight.setBackground(OSRS_NEWS_BACKGROUND);
break;
default:
if (RUNELITE_ICON != null)
{
avatar.setIcon(new ImageIcon(RUNELITE_ICON));
avatar.setIcon(RUNELITE_ICON);
}
avatarAndRight.setBackground(BLOG_POST_BACKGROUND);
break;
@@ -296,7 +350,7 @@ class FeedPanel extends PluginPanel
}
});
add(avatarAndRight);
feedContainer.add(avatarAndRight);
}
private String durationToString(Duration duration)

View File

@@ -93,6 +93,7 @@ public class FeedPlugin extends Plugin
navButton = NavigationButton.builder()
.name("News Feed")
.icon(icon)
.priority(8)
.panel(feedPanel)
.build();

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, Seth <https://github.com/sethtroll>
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,14 +33,17 @@ import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.game.AsyncBufferedImage;
import net.runelite.client.util.LinkBrowser;
import net.runelite.client.util.StackFormatter;
/**
* This panel displays an individual item result in the
* Grand Exchange search plugin.
*/
@Slf4j
class GrandExchangeItemPanel extends JPanel
{
@@ -52,7 +56,8 @@ class GrandExchangeItemPanel extends JPanel
layout.setHgap(5);
setLayout(layout);
setToolTipText(name);
setBackground(ColorScheme.MEDIUM_GRAY_COLOR);
Color background = getBackground();
addMouseListener(new MouseAdapter()
@@ -60,7 +65,7 @@ class GrandExchangeItemPanel extends JPanel
@Override
public void mouseEntered(MouseEvent e)
{
setBackground(getBackground().darker().darker());
setBackground(getBackground().brighter());
}
@Override
@@ -76,11 +81,7 @@ class GrandExchangeItemPanel extends JPanel
}
});
setBorder(new CompoundBorder
(
new LineBorder(getBackground().brighter(), 1),
new EmptyBorder(5, 5, 5, 5)
));
setBorder(new EmptyBorder(5, 5, 5, 0));
// Icon
JLabel itemIcon = new JLabel();
@@ -97,6 +98,9 @@ class GrandExchangeItemPanel extends JPanel
// Item name
JLabel itemName = new JLabel();
itemName.setForeground(Color.WHITE);
itemName.setMaximumSize(new Dimension(0, 0)); // to limit the label's size for
itemName.setPreferredSize(new Dimension(0, 0)); // items with longer names
itemName.setText(name);
rightPanel.add(itemName);
@@ -110,13 +114,13 @@ class GrandExchangeItemPanel extends JPanel
{
gePriceLabel.setText("N/A");
}
gePriceLabel.setForeground(Color.GREEN);
gePriceLabel.setForeground(ColorScheme.GRAND_EXCHANGE_PRICE);
rightPanel.add(gePriceLabel);
// Alch price
JLabel haPriceLabel = new JLabel();
haPriceLabel.setText(StackFormatter.formatNumber(haPrice.intValue()) + " alch");
haPriceLabel.setForeground(Color.orange);
haPriceLabel.setForeground(ColorScheme.GRAND_EXCHANGE_ALCH);
rightPanel.add(haPriceLabel);
add(rightPanel, BorderLayout.CENTER);
@@ -131,4 +135,4 @@ class GrandExchangeItemPanel extends JPanel
LinkBrowser.browse(url);
}
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,38 +29,73 @@ package net.runelite.client.plugins.grandexchange;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.annotation.Nullable;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.border.TitledBorder;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.GrandExchangeOffer;
import net.runelite.api.GrandExchangeOfferState;
import static net.runelite.api.GrandExchangeOfferState.CANCELLED_BUY;
import static net.runelite.api.GrandExchangeOfferState.CANCELLED_SELL;
import static net.runelite.api.GrandExchangeOfferState.EMPTY;
import net.runelite.api.ItemComposition;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.components.ThinProgressBar;
import net.runelite.client.util.StackFormatter;
import net.runelite.client.util.SwingUtil;
@Slf4j
public class GrandExchangeOfferSlot extends JPanel
{
private static final Color GE_INPROGRESS_ORANGE = new Color(0xd8, 0x80, 0x20).brighter();
private static final Color GE_FINISHED_GREEN = new Color(0, 0x5f, 0);
private static final Color GE_CANCELLED_RED = new Color(0x8f, 0, 0);
private static final String FACE_CARD = "FACE_CARD";
private static final String DETAILS_CARD = "DETAILS_CARD";
private static final String INFO_CARD = "INFO_CARD";
private static final String EMPTY_CARD = "EMPTY_CARD";
private static final ImageIcon RIGHT_ARROW_ICON;
private static final ImageIcon LEFT_ARROW_ICON;
private final JPanel container = new JPanel();
private final CardLayout cardLayout = new CardLayout();
private final JLabel itemIcon = new JLabel();
private final TitledBorder itemName = BorderFactory.createTitledBorder("Nothing");
private final JLabel offerState = new JLabel("Text so the label has height");
private final JProgressBar progressBar = new JProgressBar();
private final JLabel itemName = new JLabel();
private final JLabel offerInfo = new JLabel();
private final JLabel switchFaceViewIcon = new JLabel();
private final JLabel itemPrice = new JLabel();
private final JLabel offerSpent = new JLabel();
private final JLabel switchDetailsViewIcon = new JLabel();
private final ThinProgressBar progressBar = new ThinProgressBar();
private boolean showingFace = true;
static
{
try
{
synchronized (ImageIO.class)
{
RIGHT_ARROW_ICON = new ImageIcon(ImageIO.read(GrandExchangeOfferSlot.class.getResourceAsStream("arrow_right.png")));
LEFT_ARROW_ICON = new ImageIcon(ImageIO.read(GrandExchangeOfferSlot.class.getResourceAsStream("arrow_left.png")));
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
/**
* This (sub)panel is used for each GE slot displayed
@@ -72,123 +108,188 @@ public class GrandExchangeOfferSlot extends JPanel
private void buildPanel()
{
setBorder(BorderFactory.createCompoundBorder(
// Add a margin underneath each slot panel to space them out
BorderFactory.createEmptyBorder(0, 0, 3, 0),
itemName
));
setLayout(new BorderLayout());
setBackground(ColorScheme.DARK_GRAY_COLOR);
setBorder(new EmptyBorder(7, 0, 0, 0));
// The default border color is kind of dark, so we change it to something lighter
itemName.setBorder(BorderFactory.createLineBorder(getBackground().brighter()));
final MouseListener ml = new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent mouseEvent)
{
super.mousePressed(mouseEvent);
switchPanel();
}
progressBar.setStringPainted(true);
@Override
public void mouseEntered(MouseEvent mouseEvent)
{
super.mouseEntered(mouseEvent);
container.setBackground(ColorScheme.MEDIUM_GRAY_COLOR.brighter());
}
setLayout(cardLayout);
@Override
public void mouseExited(MouseEvent mouseEvent)
{
super.mouseExited(mouseEvent);
container.setBackground(ColorScheme.MEDIUM_GRAY_COLOR);
}
};
// Card for when the slot has an offer in it
JPanel infoCard = new JPanel();
add(infoCard, INFO_CARD);
// Add padding to give the icon and progress bar room to breathe
infoCard.setBorder(BorderFactory.createEmptyBorder(0, 2, 2, 2));
container.setLayout(cardLayout);
container.setBackground(ColorScheme.MEDIUM_GRAY_COLOR);
infoCard.setLayout(new BoxLayout(infoCard, BoxLayout.X_AXIS));
// Icon on the left
infoCard.add(itemIcon);
JPanel faceCard = new JPanel();
faceCard.setOpaque(false);
faceCard.setLayout(new BorderLayout());
faceCard.addMouseListener(ml);
// Info on the right
JPanel offerStatePanel = new JPanel();
offerStatePanel.setLayout(new BoxLayout(offerStatePanel, BoxLayout.Y_AXIS));
offerStatePanel.add(offerState);
offerStatePanel.add(progressBar);
infoCard.add(offerStatePanel);
itemIcon.setVerticalAlignment(JLabel.CENTER);
itemIcon.setHorizontalAlignment(JLabel.CENTER);
itemIcon.setPreferredSize(new Dimension(45, 45));
// Card for when the slot is empty
JPanel emptySlotCard = new JPanel();
add(emptySlotCard, EMPTY_CARD);
// Counteract the height lost to the text at the top of the TitledBorder
int itemNameBorderHeight = itemName.getBorderInsets(this).top;
emptySlotCard.setBorder(BorderFactory.createEmptyBorder(0, 0, (itemNameBorderHeight - 1) / 2, 0));
// Center the "Empty" label horizontally
emptySlotCard.setLayout( new BoxLayout(emptySlotCard, BoxLayout.X_AXIS));
emptySlotCard.add(Box.createHorizontalGlue());
emptySlotCard.add(new JLabel(getNameForState(EMPTY)), BorderLayout.CENTER);
emptySlotCard.add(Box.createHorizontalGlue());
itemName.setForeground(Color.WHITE);
itemName.setVerticalAlignment(JLabel.BOTTOM);
itemName.setFont(FontManager.getRunescapeSmallFont());
cardLayout.show(this, EMPTY_CARD);
offerInfo.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
offerInfo.setVerticalAlignment(JLabel.TOP);
offerInfo.setFont(FontManager.getRunescapeSmallFont());
switchFaceViewIcon.setIcon(RIGHT_ARROW_ICON);
switchFaceViewIcon.setVerticalAlignment(JLabel.CENTER);
switchFaceViewIcon.setHorizontalAlignment(JLabel.CENTER);
switchFaceViewIcon.setPreferredSize(new Dimension(30, 45));
JPanel offerFaceDetails = new JPanel();
offerFaceDetails.setOpaque(false);
offerFaceDetails.setLayout(new GridLayout(2, 1, 0, 2));
offerFaceDetails.add(itemName);
offerFaceDetails.add(offerInfo);
faceCard.add(offerFaceDetails, BorderLayout.CENTER);
faceCard.add(itemIcon, BorderLayout.WEST);
faceCard.add(switchFaceViewIcon, BorderLayout.EAST);
JPanel detailsCard = new JPanel();
detailsCard.setOpaque(false);
detailsCard.setLayout(new BorderLayout());
detailsCard.setBorder(new EmptyBorder(0, 15, 0, 0));
detailsCard.addMouseListener(ml);
itemPrice.setForeground(Color.WHITE);
itemPrice.setVerticalAlignment(JLabel.BOTTOM);
itemPrice.setFont(FontManager.getRunescapeSmallFont());
offerSpent.setForeground(Color.WHITE);
offerSpent.setVerticalAlignment(JLabel.TOP);
offerSpent.setFont(FontManager.getRunescapeSmallFont());
switchDetailsViewIcon.setIcon(LEFT_ARROW_ICON);
switchDetailsViewIcon.setVerticalAlignment(JLabel.CENTER);
switchDetailsViewIcon.setHorizontalAlignment(JLabel.CENTER);
switchDetailsViewIcon.setPreferredSize(new Dimension(30, 45));
JPanel offerDetails = new JPanel();
offerDetails.setOpaque(false);
offerDetails.setLayout(new GridLayout(2, 1));
offerDetails.add(itemPrice);
offerDetails.add(offerSpent);
detailsCard.add(offerDetails, BorderLayout.CENTER);
detailsCard.add(switchDetailsViewIcon, BorderLayout.EAST);
container.add(faceCard, FACE_CARD);
container.add(detailsCard, DETAILS_CARD);
cardLayout.show(container, FACE_CARD);
add(container, BorderLayout.CENTER);
add(progressBar, BorderLayout.SOUTH);
}
void updateOffer(ItemComposition offerItem, BufferedImage itemImage, @Nullable GrandExchangeOffer newOffer)
{
if (newOffer == null || newOffer.getState() == EMPTY)
{
cardLayout.show(this, EMPTY_CARD);
itemName.setTitle("Nothing");
return;
}
else
{
cardLayout.show(this, INFO_CARD);
cardLayout.show(container, FACE_CARD);
itemName.setTitle(offerItem.getName());
itemName.setText(offerItem.getName());
itemIcon.setIcon(new ImageIcon(itemImage));
boolean shouldStack = offerItem.isStackable() || newOffer.getTotalQuantity() > 1;
ImageIcon newItemIcon = new ImageIcon(itemImage);
itemIcon.setIcon(newItemIcon);
boolean buying = newOffer.getState() == GrandExchangeOfferState.BOUGHT
|| newOffer.getState() == GrandExchangeOfferState.BUYING
|| newOffer.getState() == GrandExchangeOfferState.CANCELLED_BUY;
offerState.setText(getNameForState(newOffer.getState())
+ " at "
+ StackFormatter.formatNumber(newOffer.getState() == GrandExchangeOfferState.BOUGHT ? (newOffer.getSpent() / newOffer.getTotalQuantity()) : newOffer.getPrice())
+ (newOffer.getTotalQuantity() > 1 ? " gp ea" : " gp"));
String offerState = (buying ? "Bought " : "Sold ")
+ StackFormatter.quantityToRSDecimalStack(newOffer.getQuantitySold()) + " / "
+ StackFormatter.quantityToRSDecimalStack(newOffer.getTotalQuantity());
progressBar.setMaximum(newOffer.getTotalQuantity());
offerInfo.setText(offerState);
itemPrice.setText(htmlLabel("Price each: ", newOffer.getPrice() + ""));
String action = buying ? "Spent: " : "Received: ";
offerSpent.setText(htmlLabel(action, StackFormatter.formatNumber(newOffer.getSpent()) + " / "
+ StackFormatter.formatNumber(newOffer.getPrice() * newOffer.getTotalQuantity())));
progressBar.setForeground(getProgressColor(newOffer));
progressBar.setMaximumValue(newOffer.getTotalQuantity());
progressBar.setValue(newOffer.getQuantitySold());
progressBar.setBackground(getColorForState(newOffer.getState()));
progressBar.setString(newOffer.getQuantitySold() + "/" + newOffer.getTotalQuantity());
progressBar.update();
/* Couldn't set the tooltip for the container panel as the children override it, so I'm setting
* the tooltips on the children instead. */
for (Component c : container.getComponents())
{
if (c instanceof JPanel)
{
JPanel panel = (JPanel) c;
panel.setToolTipText(htmlTooltip(((int) progressBar.getPercentage()) + "%"));
}
}
}
revalidate();
repaint();
}
private String getNameForState(GrandExchangeOfferState state)
private String htmlTooltip(String value)
{
switch (state)
{
case CANCELLED_BUY:
return "Buying cancelled";
case CANCELLED_SELL:
return "Selling cancelled";
case BUYING:
return "Buying";
case BOUGHT:
return "Bought";
case SELLING:
return "Selling";
case SOLD:
return "Sold";
case EMPTY:
default:
return "Empty";
}
return "<html><body style = 'color:" + SwingUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR) + "'>Progress: <span style = 'color:white'>" + value + "</span></body></html>";
}
private Color getColorForState(GrandExchangeOfferState state)
private String htmlLabel(String key, String value)
{
switch (state)
{
case CANCELLED_BUY:
case CANCELLED_SELL:
return GE_CANCELLED_RED;
case BUYING:
case SELLING:
return GE_INPROGRESS_ORANGE;
case BOUGHT:
case SOLD:
return GE_FINISHED_GREEN;
case EMPTY:
default:
return null;
}
return "<html><body style = 'color:white'>" + key + "<span style = 'color:" + SwingUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR) + "'>" + value + "</span></body></html>";
}
private void switchPanel()
{
this.showingFace = !this.showingFace;
cardLayout.show(container, showingFace ? FACE_CARD : DETAILS_CARD);
}
private Color getProgressColor(GrandExchangeOffer offer)
{
if (offer.getState() == CANCELLED_BUY || offer.getState() == CANCELLED_SELL)
{
return ColorScheme.PROGRESS_ERROR_COLOR;
}
if (offer.getQuantitySold() == offer.getTotalQuantity())
{
return ColorScheme.PROGRESS_COMPLETE_COLOR;
}
return ColorScheme.PROGRESS_INPROGRESS_COLOR;
}
}

View File

@@ -0,0 +1,201 @@
/*
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* Copyright (c) 2018, Psikoi <https://github.com/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.grandexchange;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.image.BufferedImage;
import java.util.concurrent.ScheduledExecutorService;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import net.runelite.api.Client;
import net.runelite.api.GrandExchangeOffer;
import net.runelite.api.GrandExchangeOfferState;
import net.runelite.api.ItemComposition;
import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.components.PluginErrorPanel;
public class GrandExchangeOffersPanel extends JPanel
{
private static final String ERROR_PANEL = "ERROR_PANEL";
private static final String OFFERS_PANEL = "OFFERS_PANEL";
private static final int MAX_OFFERS = 8;
private final GridBagConstraints constraints = new GridBagConstraints();
private final CardLayout cardLayout = new CardLayout();
/* The offers container, this will hold all the individual ge offers panels */
private final JPanel offerPanel = new JPanel();
/* The error panel, this displays an error message */
private final PluginErrorPanel errorPanel = new PluginErrorPanel();
/* The center panel, this holds either the error panel or the offers container */
private final JPanel container = new JPanel(cardLayout);
private final Client client;
private final ItemManager itemManager;
private final ScheduledExecutorService executor;
private GrandExchangeOfferSlot[] offerSlotPanels = new GrandExchangeOfferSlot[MAX_OFFERS];
public GrandExchangeOffersPanel(Client client, ItemManager itemManager, ScheduledExecutorService executor)
{
this.client = client;
this.itemManager = itemManager;
this.executor = executor;
init();
}
void init()
{
setLayout(new BorderLayout());
setBackground(ColorScheme.DARK_GRAY_COLOR);
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.weightx = 1;
constraints.gridx = 0;
constraints.gridy = 0;
/* This panel wraps the offers panel and limits its height */
JPanel offersWrapper = new JPanel(new BorderLayout());
offersWrapper.setBackground(ColorScheme.DARK_GRAY_COLOR);
offersWrapper.add(offerPanel, BorderLayout.NORTH);
offerPanel.setLayout(new GridBagLayout());
offerPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
offerPanel.setBackground(ColorScheme.DARK_GRAY_COLOR);
/* This panel wraps the error panel and limits its height */
JPanel errorWrapper = new JPanel(new BorderLayout());
errorWrapper.setBackground(ColorScheme.DARK_GRAY_COLOR);
errorWrapper.add(errorPanel, BorderLayout.NORTH);
errorPanel.setBorder(new EmptyBorder(50, 20, 20, 20));
errorPanel.setContent("No offers detected", "No grand exchange offers were found on your account.");
container.add(offersWrapper, OFFERS_PANEL);
container.add(errorWrapper, ERROR_PANEL);
add(container, BorderLayout.CENTER);
resetOffers();
}
void resetOffers()
{
offerPanel.removeAll();
for (int i = 0; i < offerSlotPanels.length; i++)
{
offerSlotPanels[i] = null;
}
updateEmptyOffersPanel();
}
void updateOffer(ItemComposition item, BufferedImage itemImage, GrandExchangeOffer newOffer, int slot)
{
/* If slot was previously filled, and is now empty, remove it from the list */
if (newOffer == null || newOffer.getState() == GrandExchangeOfferState.EMPTY)
{
if (offerSlotPanels[slot] != null)
{
offerPanel.remove(offerSlotPanels[slot]);
offerSlotPanels[slot] = null;
revalidate();
repaint();
}
removeTopMargin();
updateEmptyOffersPanel();
return;
}
/* If slot was empty, and is now filled, add it to the list */
if (offerSlotPanels[slot] == null)
{
GrandExchangeOfferSlot newSlot = new GrandExchangeOfferSlot();
offerSlotPanels[slot] = newSlot;
offerPanel.add(newSlot, constraints);
constraints.gridy++;
}
offerSlotPanels[slot].updateOffer(item, itemImage, newOffer);
removeTopMargin();
revalidate();
repaint();
updateEmptyOffersPanel();
}
/**
* Reset the border for the first offer slot.
*/
private void removeTopMargin()
{
if (offerPanel.getComponentCount() <= 0)
{
return;
}
JPanel firstItem = (JPanel) offerPanel.getComponent(0);
firstItem.setBorder(null);
}
/**
* This method calculates the amount of empty ge offer slots, if all slots are empty,
* it shows the error panel.
*/
private void updateEmptyOffersPanel()
{
int nullCount = 0;
for (GrandExchangeOfferSlot slot : offerSlotPanels)
{
if (slot == null)
{
nullCount++;
}
}
if (nullCount == MAX_OFFERS)
{
offerPanel.removeAll();
cardLayout.show(container, ERROR_PANEL);
}
else
{
cardLayout.show(container, OFFERS_PANEL);
}
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,58 +27,58 @@
package net.runelite.client.plugins.grandexchange;
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.border.EmptyBorder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GrandExchangeOffer;
import net.runelite.api.ItemComposition;
import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.materialtabs.MaterialTab;
import net.runelite.client.ui.components.materialtabs.MaterialTabGroup;
@Slf4j
class GrandExchangePanel extends PluginPanel
{
private static final int MAX_OFFERS = 8;
// this panel will hold either the ge search panel or the ge offers panel
private final JPanel display = new JPanel();
private final MaterialTabGroup tabGroup = new MaterialTabGroup(display);
private final MaterialTab searchTab;
@Getter
private GrandExchangeSearchPanel searchPanel;
private GrandExchangeOfferSlot[] offerSlotPanels = new GrandExchangeOfferSlot[MAX_OFFERS];
private JPanel offerPanel = new JPanel();
private JTabbedPane tabbedPane = new JTabbedPane();
@Getter
private GrandExchangeOffersPanel offersPanel;
@Inject
GrandExchangePanel(Client client, ItemManager itemManager, ScheduledExecutorService executor)
{
setLayout(new BorderLayout());
add(tabbedPane, BorderLayout.NORTH);
super(false);
// Offer Panel
offerPanel.setLayout(new BoxLayout(offerPanel, BoxLayout.Y_AXIS));
for (int i = 0; i < offerSlotPanels.length; ++i)
{
offerSlotPanels[i] = new GrandExchangeOfferSlot();
offerPanel.add(offerSlotPanels[i]);
}
setLayout(new BorderLayout());
setBackground(ColorScheme.DARK_GRAY_COLOR);
// Search Panel
searchPanel = new GrandExchangeSearchPanel(client, itemManager, executor);
tabbedPane.addTab("Offers", offerPanel);
tabbedPane.addTab("Search", searchPanel);
}
//Offers Panel
offersPanel = new GrandExchangeOffersPanel(client, itemManager, executor);
void updateOffer(ItemComposition item, BufferedImage itemImage, GrandExchangeOffer newOffer, int slot)
{
offerSlotPanels[slot].updateOffer(item, itemImage, newOffer);
MaterialTab offersTab = new MaterialTab("Offers", tabGroup, offersPanel);
searchTab = new MaterialTab("Search", tabGroup, searchPanel);
tabGroup.setBorder(new EmptyBorder(5, 0, 0, 0));
tabGroup.addTab(offersTab);
tabGroup.addTab(searchTab);
tabGroup.select(offersTab); // selects the default selected tab
add(tabGroup, BorderLayout.NORTH);
add(display, BorderLayout.CENTER);
}
void showSearch()
@@ -87,7 +88,7 @@ class GrandExchangePanel extends PluginPanel
return;
}
tabbedPane.setSelectedComponent(searchPanel);
tabGroup.select(searchTab);
revalidate();
}
}
}

View File

@@ -44,6 +44,7 @@ import net.runelite.api.ItemComposition;
import net.runelite.api.MenuEntry;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.FocusChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GrandExchangeOfferChanged;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.widgets.WidgetID;
@@ -113,6 +114,7 @@ public class GrandExchangePlugin extends Plugin
button = NavigationButton.builder()
.name("GE Offers")
.icon(icon)
.priority(3)
.panel(panel)
.build();
@@ -161,7 +163,16 @@ public class GrandExchangePlugin extends Plugin
ItemComposition offerItem = itemManager.getItemComposition(offer.getItemId());
boolean shouldStack = offerItem.isStackable() || offer.getTotalQuantity() > 1;
BufferedImage itemImage = itemManager.getImage(offer.getItemId(), offer.getTotalQuantity(), shouldStack);
SwingUtilities.invokeLater(() -> panel.updateOffer(offerItem, itemImage, offerEvent.getOffer(), offerEvent.getSlot()));
SwingUtilities.invokeLater(() -> panel.getOffersPanel().updateOffer(offerItem, itemImage, offerEvent.getOffer(), offerEvent.getSlot()));
}
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
{
if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN)
{
panel.getOffersPanel().resetOffers();
}
}
@Subscribe

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, Seth <https://github.com/sethtroll>
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,19 +27,19 @@ package net.runelite.client.plugins.grandexchange;
import com.google.common.base.Strings;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
@@ -46,26 +47,69 @@ import net.runelite.api.Client;
import net.runelite.api.ItemComposition;
import net.runelite.client.game.AsyncBufferedImage;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.hiscore.IconTextField;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.components.IconTextField;
import net.runelite.client.ui.components.PluginErrorPanel;
import net.runelite.http.api.item.Item;
import net.runelite.http.api.item.ItemPrice;
import net.runelite.http.api.item.SearchResult;
/**
* This panel holds the search section of the Grand Exchange Plugin.
* It should display a search bar and either item results or a error panel.
*/
@Slf4j
class GrandExchangeSearchPanel extends JPanel
{
private static final List<GrandExchangeItems> ITEMS_LIST = new ArrayList<>();
private static final String ERROR_PANEL = "ERROR_PANEL";
private static final String RESULTS_PANEL = "RESULTS_PANEL";
private static final ImageIcon SEARCH_ICON;
private static final ImageIcon LOADING_ICON;
private static final ImageIcon ERROR_ICON;
private final GridBagConstraints constraints = new GridBagConstraints();
private final CardLayout cardLayout = new CardLayout();
private final Client client;
private final ItemManager itemManager;
private final ScheduledExecutorService executor;
private Icon search;
private final IconTextField searchBox = new IconTextField();
private IconTextField searchBox = new IconTextField();
private JPanel container = new JPanel();
private JPanel searchItemsPanel = new JPanel();
private JLabel searchingLabel = new JLabel();
/* The main container, this holds the search bar and the center panel */
private final JPanel container = new JPanel();
/* The results container, this will hold all the individual ge item panels */
private final JPanel searchItemsPanel = new JPanel();
/* The center panel, this holds either the error panel or the results container */
private final JPanel centerPanel = new JPanel(cardLayout);
/* The error panel, this displays an error message */
private final PluginErrorPanel errorPanel = new PluginErrorPanel();
/* The results wrapper, this scrolling panel wraps the results container */
private JScrollPane resultsWrapper;
private List<GrandExchangeItems> itemsList = new ArrayList<>();
static
{
try
{
synchronized (ImageIO.class)
{
SEARCH_ICON = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search_darker.png")));
LOADING_ICON = new ImageIcon(IconTextField.class.getResource("loading_spinner.gif"));
ERROR_ICON = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("error.png")));
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
GrandExchangeSearchPanel(Client client, ItemManager itemManager, ScheduledExecutorService executor)
{
@@ -78,38 +122,54 @@ class GrandExchangeSearchPanel extends JPanel
void init()
{
setLayout(new BorderLayout());
container.setLayout(new BorderLayout());
setBackground(ColorScheme.DARK_GRAY_COLOR);
// Search Box
try
{
BufferedImage icon;
synchronized (ImageIO.class)
{
icon = ImageIO.read(GrandExchangePlugin.class.getResourceAsStream("search.png"));
}
search = new ImageIcon(icon);
}
catch (IOException e)
{
log.warn("Failed to read icon", e);
}
container.setLayout(new BorderLayout(5, 5));
container.setBorder(new EmptyBorder(10, 10, 10, 10));
container.setBackground(ColorScheme.DARK_GRAY_COLOR);
searchBox.setIcon(search);
searchBox.setPreferredSize(new Dimension(100, 30));
searchBox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR);
searchBox.setHoverBackgroundColor(ColorScheme.MEDIUM_GRAY_COLOR.brighter());
searchBox.setIcon(SEARCH_ICON);
searchBox.addActionListener(e -> executor.execute(() -> priceLookup(false)));
searchItemsPanel.setLayout(new GridBagLayout());
searchItemsPanel.setBackground(ColorScheme.DARK_GRAY_COLOR);
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.weightx = 1;
constraints.gridx = 0;
constraints.gridy = 0;
/* This panel wraps the results panel and guarantees the scrolling behaviour */
JPanel wrapper = new JPanel(new BorderLayout());
wrapper.setBackground(ColorScheme.DARK_GRAY_COLOR);
wrapper.add(searchItemsPanel, BorderLayout.NORTH);
resultsWrapper = new JScrollPane(wrapper);
resultsWrapper.setBackground(ColorScheme.DARK_GRAY_COLOR);
resultsWrapper.getVerticalScrollBar().setPreferredSize(new Dimension(12, 0));
resultsWrapper.getVerticalScrollBar().setBorder(new EmptyBorder(0, 5, 0, 0));
resultsWrapper.setVisible(false);
/* This panel wraps the error panel and limits its height */
JPanel errorWrapper = new JPanel(new BorderLayout());
errorWrapper.setBackground(ColorScheme.DARK_GRAY_COLOR);
errorWrapper.add(errorPanel, BorderLayout.NORTH);
errorPanel.setContent("Grand Exchange Search",
"Here you can search for an item by its name to find price information.");
centerPanel.add(resultsWrapper, RESULTS_PANEL);
centerPanel.add(errorWrapper, ERROR_PANEL);
cardLayout.show(centerPanel, ERROR_PANEL);
container.add(searchBox, BorderLayout.NORTH);
container.add(centerPanel, BorderLayout.CENTER);
// Searching label
searchingLabel.setHorizontalAlignment(JLabel.CENTER);
searchingLabel.setForeground(Color.YELLOW);
// Items Panel
searchItemsPanel.setLayout(new GridLayout(0, 1, 0, 3));
searchItemsPanel.setBorder(new EmptyBorder(3, 0, 0, 0));
container.add(searchItemsPanel, BorderLayout.SOUTH);
add(container, BorderLayout.NORTH);
add(container, BorderLayout.CENTER);
}
void priceLookup(String item)
@@ -130,7 +190,9 @@ class GrandExchangeSearchPanel extends JPanel
// Input is not empty, add searching label
searchItemsPanel.removeAll();
showSearchString("Searching...");
searchBox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR);
searchBox.setEditable(false);
searchBox.setIcon(LOADING_ICON);
SearchResult result;
@@ -141,13 +203,19 @@ class GrandExchangeSearchPanel extends JPanel
catch (ExecutionException ex)
{
log.warn("Unable to search for item {}", lookup, ex);
showSearchString("Error performing search");
searchBox.setIcon(ERROR_ICON);
searchBox.setEditable(true);
errorPanel.setContent("Error fetching results", "An error occured why trying to fetch item data, please try again later.");
cardLayout.show(centerPanel, ERROR_PANEL);
return;
}
itemsList.clear();
if (result != null && !result.getItems().isEmpty())
{
cardLayout.show(centerPanel, RESULTS_PANEL);
for (Item item : result.getItems())
{
int itemId = item.getId();
@@ -170,7 +238,7 @@ class GrandExchangeSearchPanel extends JPanel
AsyncBufferedImage itemImage = itemManager.getImage(itemId);
ITEMS_LIST.add(new GrandExchangeItems(itemImage, item.getName(), itemId, itemPrice != null ? itemPrice.getPrice() : 0, itemComp.getPrice() * 0.6));
itemsList.add(new GrandExchangeItems(itemImage, item.getName(), itemId, itemPrice != null ? itemPrice.getPrice() : 0, itemComp.getPrice() * 0.6));
// If using hotkey to lookup item, stop after finding match.
if (exactMatch && item.getName().equalsIgnoreCase(lookup))
@@ -179,44 +247,51 @@ class GrandExchangeSearchPanel extends JPanel
}
}
}
else
{
searchBox.setIcon(ERROR_ICON);
errorPanel.setContent("No results found.", "No items were found with that name, please try again.");
cardLayout.show(centerPanel, ERROR_PANEL);
}
SwingUtilities.invokeLater(() ->
{
if (ITEMS_LIST.isEmpty())
int index = 0;
for (GrandExchangeItems item : itemsList)
{
showSearchString("No results found.");
}
else
{
for (GrandExchangeItems item : ITEMS_LIST)
GrandExchangeItemPanel panel = new GrandExchangeItemPanel(item.getIcon(), item.getName(),
item.getItemId(), item.getGePrice(), item.getHaPrice());
/*
Add the first item directly, wrap the rest with margin. This margin hack is because
gridbaglayout does not support inter-element margins.
*/
if (index++ > 0)
{
GrandExchangeItemPanel panel = new GrandExchangeItemPanel(item.getIcon(), item.getName(),
item.getItemId(), item.getGePrice(), item.getHaPrice());
searchItemsPanel.add(panel);
JPanel marginWrapper = new JPanel(new BorderLayout());
marginWrapper.setBackground(ColorScheme.DARK_GRAY_COLOR);
marginWrapper.setBorder(new EmptyBorder(5, 0, 0, 0));
marginWrapper.add(panel, BorderLayout.NORTH);
searchItemsPanel.add(marginWrapper, constraints);
}
else
{
searchItemsPanel.add(panel, constraints);
}
constraints.gridy++;
}
// Remove searching label after search is complete
showSearchString(null);
ITEMS_LIST.clear();
// remove focus from the search bar
searchItemsPanel.requestFocusInWindow();
searchBox.setEditable(true);
// Remove searching label after search is complete
if (!itemsList.isEmpty())
{
searchBox.setIcon(SEARCH_ICON);
}
});
}
private void showSearchString(String str)
{
if (str != null)
{
remove(searchingLabel);
searchingLabel.setText(str);
add(searchingLabel, BorderLayout.CENTER);
}
else
{
remove(searchingLabel);
}
revalidate();
repaint();
}
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,10 +26,8 @@
package net.runelite.client.plugins.hiscore;
import com.google.common.base.Strings;
import java.awt.BorderLayout;
import java.awt.Color;
import com.google.common.collect.ImmutableList;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
@@ -39,31 +38,24 @@ import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.MouseInputAdapter;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.Experience;
import net.runelite.api.Player;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.IconTextField;
import net.runelite.client.util.StackFormatter;
import net.runelite.http.api.hiscore.HiscoreClient;
import net.runelite.http.api.hiscore.HiscoreEndpoint;
@@ -102,13 +94,17 @@ import net.runelite.http.api.hiscore.Skill;
@Slf4j
public class HiscorePanel extends PluginPanel
{
private static final String SKILL_NAME = "SKILL_NAME";
private static final String SKILL = "SKILL";
/* The maximum allowed username length in runescape accounts */
private static final int MAX_USERNAME_LENGTH = 12;
private static final ImageIcon SEARCH_ICON;
private static final ImageIcon LOADING_ICON;
private static final ImageIcon ERROR_ICON;
/**
* Real skills, ordered in the way they should be displayed in the panel.
*/
private static final Set<HiscoreSkill> SKILLS = new LinkedHashSet<>(Arrays.asList(
private static final List<HiscoreSkill> SKILLS = ImmutableList.of(
ATTACK, HITPOINTS, MINING,
STRENGTH, AGILITY, SMITHING,
DEFENCE, HERBLORE, FISHING,
@@ -117,7 +113,7 @@ public class HiscorePanel extends PluginPanel
MAGIC, FLETCHING, WOODCUTTING,
RUNECRAFT, SLAYER, FARMING,
CONSTRUCTION, HUNTER
));
);
@Inject
ScheduledExecutorService executor;
@@ -132,24 +128,45 @@ public class HiscorePanel extends PluginPanel
private final List<JLabel> skillLabels = new ArrayList<>();
private final JPanel statsPanel = new JPanel();
private final ButtonGroup endpointButtonGroup = new ButtonGroup();
private final JTextArea details = new JTextArea();
private final JProgressBar progressBar;
private List<JToggleButton> endpointButtons;
/* A list of all the selectable endpoints (ironman, deadman, etc) */
private final List<JPanel> endPoints = new ArrayList<>();
private final HiscoreClient hiscoreClient = new HiscoreClient();
private HiscoreResult result;
/* The currently selected endpoint */
private HiscoreEndpoint selectedEndPoint;
/* Used to prevent users from switching endpoint tabs while the results are loading */
private boolean loading = false;
static
{
try
{
synchronized (ImageIO.class)
{
SEARCH_ICON = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search.png")));
LOADING_ICON = new ImageIcon(IconTextField.class.getResource("loading_spinner_darker.gif"));
ERROR_ICON = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("error.png")));
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
@Inject
public HiscorePanel(HiscoreConfig config)
{
super();
this.config = config;
// Panel "constants"
// This was an EtchedBorder, but the style would change when the window was maximized.
Border subPanelBorder = BorderFactory.createLineBorder(this.getBackground().brighter(), 2);
setBorder(new EmptyBorder(10, 10, 0, 10));
setBackground(ColorScheme.DARK_GRAY_COLOR);
// Create GBL to arrange sub items
GridBagLayout gridBag = new GridBagLayout();
@@ -160,28 +177,11 @@ public class HiscorePanel extends PluginPanel
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.NORTH;
// Search box
JPanel inputPanel = new JPanel();
inputPanel.setLayout(new BorderLayout(7, 7));
inputPanel.setBorder(subPanelBorder);
Icon search;
try
{
BufferedImage icon;
synchronized (ImageIO.class)
{
icon = ImageIO.read(HiscorePanel.class.getResourceAsStream("search.png"));
}
search = new ImageIcon(icon);
}
catch (IOException ex)
{
throw new RuntimeException(ex);
}
input = new IconTextField();
input.setIcon(search);
input.setPreferredSize(new Dimension(100, 30));
input.setBackground(ColorScheme.DARKER_GRAY_COLOR);
input.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR);
input.setIcon(SEARCH_ICON);
input.addActionListener(e -> executor.execute(this::lookup));
input.addMouseListener(new MouseAdapter()
{
@@ -192,7 +192,6 @@ public class HiscorePanel extends PluginPanel
{
return;
}
if (client == null)
{
return;
@@ -206,97 +205,20 @@ public class HiscorePanel extends PluginPanel
}
}
});
inputPanel.add(input, BorderLayout.CENTER);
c.gridx = 0;
c.gridy = 0;
c.weightx = 1;
c.weighty = 0;
c.insets = new Insets(0, 0, 3, 0);
gridBag.setConstraints(inputPanel, c);
add(inputPanel);
// Panel that holds skill icons
GridLayout stats = new GridLayout(8, 3);
statsPanel.setLayout(stats);
statsPanel.setBorder(subPanelBorder);
// For each skill on the ingame skill panel, create a Label and add it to the UI
for (HiscoreSkill skill : SKILLS)
{
JPanel panel = makeSkillPanel(skill.getName(), skill);
statsPanel.add(panel);
}
c.gridx = 0;
c.gridy = 1;
gridBag.setConstraints(statsPanel, c);
add(statsPanel);
JPanel totalPanel = new JPanel();
totalPanel.setBorder(subPanelBorder);
totalPanel.setLayout(new GridLayout(1, 2));
totalPanel.add(makeSkillPanel(OVERALL.getName(), OVERALL));
totalPanel.add(makeSkillPanel("Combat", null));
c.gridx = 0;
c.gridy = 2;
gridBag.setConstraints(totalPanel, c);
add(totalPanel);
JPanel minigamePanel = new JPanel();
minigamePanel.setBorder(subPanelBorder);
// These aren't all on one row because when there's a label with four or more digits it causes the details
// panel to change its size for some reason...
minigamePanel.setLayout(new GridLayout(2, 3));
minigamePanel.add(makeSkillPanel(CLUE_SCROLL_ALL.getName(), CLUE_SCROLL_ALL));
minigamePanel.add(makeSkillPanel(LAST_MAN_STANDING.getName(), LAST_MAN_STANDING));
minigamePanel.add(makeSkillPanel(BOUNTY_HUNTER_ROGUE.getName(), BOUNTY_HUNTER_ROGUE));
minigamePanel.add(makeSkillPanel(BOUNTY_HUNTER_HUNTER.getName(), BOUNTY_HUNTER_HUNTER));
c.gridx = 0;
c.gridy = 3;
gridBag.setConstraints(minigamePanel, c);
add(minigamePanel);
JPanel detailsPanel = new JPanel();
detailsPanel.setBorder(subPanelBorder);
detailsPanel.setLayout(new BorderLayout());
// Rather than using one JLabel for each line, make a JTextArea look and act like a JLabel
details.setEditable(false);
details.setCursor(null);
details.setOpaque(false);
details.setFocusable(false);
details.setWrapStyleWord(true);
details.setLineWrap(true);
details.setMargin(new Insets(2, 4, 4, 4));
details.setRows(6);
details.setText("");
detailsPanel.add(details, BorderLayout.CENTER);
progressBar = new JProgressBar();
progressBar.setStringPainted(true);
progressBar.setValue(0);
progressBar.setMinimum(0);
progressBar.setMaximum(100);
progressBar.setBackground(Color.RED);
progressBar.setVisible(false);
detailsPanel.add(progressBar, BorderLayout.SOUTH);
c.gridx = 0;
c.gridy = 4;
gridBag.setConstraints(detailsPanel, c);
add(detailsPanel);
c.insets = new Insets(0, 0, 10, 0);
gridBag.setConstraints(input, c);
add(input);
/* The container for all the endpoint selectors */
JPanel endpointPanel = new JPanel();
endpointPanel.setBorder(subPanelBorder);
endpointPanel.setOpaque(false);
endpointPanel.setLayout(new GridLayout(1, 5, 7, 1));
endpointButtons = new ArrayList<>();
for (HiscoreEndpoint endpoint : HiscoreEndpoint.values())
{
try
@@ -307,25 +229,32 @@ public class HiscorePanel extends PluginPanel
iconImage = ImageIO.read(HiscorePanel.class.getResourceAsStream(
endpoint.name().toLowerCase() + ".png"));
}
JToggleButton button = new JToggleButton();
button.setIcon(new ImageIcon(iconImage));
button.setPreferredSize(new Dimension(24, 24));
button.setBackground(Color.WHITE);
button.setFocusPainted(false);
button.setActionCommand(endpoint.name());
button.setToolTipText(endpoint.getName() + " Hiscores");
button.addActionListener((e -> executor.execute(this::lookup)));
button.addMouseListener(new MouseAdapter()
JPanel panel = new JPanel();
JLabel label = new JLabel();
label.setIcon(new ImageIcon(iconImage));
panel.add(label);
panel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
panel.setToolTipText(endpoint.getName() + " Hiscores");
panel.addMouseListener(new MouseAdapter()
{
@Override
public void mouseReleased(MouseEvent e)
public void mouseClicked(MouseEvent e)
{
if (loading)
{
return;
}
executor.execute(HiscorePanel.this::lookup);
selectedEndPoint = endpoint;
updateButtons();
}
});
endpointButtons.add(button);
endpointButtonGroup.add(button);
endpointPanel.add(button);
endPoints.add(panel);
endpointPanel.add(panel);
}
catch (IOException ex)
{
@@ -333,156 +262,61 @@ public class HiscorePanel extends PluginPanel
}
}
endpointButtons.get(0).setSelected(true);
endpointButtons.get(0).setBackground(Color.CYAN);
/* Default endpoint is the general (normal) endpoint */
selectedEndPoint = HiscoreEndpoint.NORMAL;
updateButtons();
c.gridx = 0;
c.gridy = 5;
// Last item has a nonzero weighty so it will expand to fill vertical space
c.weighty = 1;
c.gridy = 1;
gridBag.setConstraints(endpointPanel, c);
add(endpointPanel);
}
void addInputKeyListener(KeyListener l)
{
this.input.addKeyListener(l);
}
// Panel that holds skill icons
GridLayout stats = new GridLayout(8, 3);
statsPanel.setLayout(stats);
statsPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
statsPanel.setBorder(new EmptyBorder(5, 0, 5, 0));
void removeInputKeyListener(KeyListener l)
{
this.input.removeKeyListener(l);
}
private void changeDetail(String skillName, HiscoreSkill skill)
{
if (result == null || result.getPlayer() == null)
// For each skill on the ingame skill panel, create a Label and add it to the UI
for (HiscoreSkill skill : SKILLS)
{
return;
JPanel panel = makeSkillPanel(skill);
panel.setOpaque(false);
statsPanel.add(panel);
}
String text;
int progress = -1;
switch (skillName)
{
case "Combat":
{
double combatLevel = Experience.getCombatLevelPrecise(
result.getAttack().getLevel(),
result.getStrength().getLevel(),
result.getDefence().getLevel(),
result.getHitpoints().getLevel(),
result.getMagic().getLevel(),
result.getRanged().getLevel(),
result.getPrayer().getLevel()
);
text = "Skill: Combat" + System.lineSeparator()
+ "Exact Combat Level: " + StackFormatter.formatNumber(combatLevel) + System.lineSeparator()
+ "Experience: " + StackFormatter.formatNumber(result.getAttack().getExperience()
+ result.getStrength().getExperience() + result.getDefence().getExperience()
+ result.getHitpoints().getExperience() + result.getMagic().getExperience()
+ result.getRanged().getExperience() + result.getPrayer().getExperience());
break;
}
case "Clue Scrolls (all)":
{
String allRank = (result.getClueScrollAll().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollAll().getRank());
String easyRank = (result.getClueScrollEasy().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollEasy().getRank());
String mediumRank = (result.getClueScrollMedium().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollMedium().getRank());
String hardRank = (result.getClueScrollHard().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollHard().getRank());
String eliteRank = (result.getClueScrollElite().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollElite().getRank());
String masterRank = (result.getClueScrollMaster().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollMaster().getRank());
String all = (result.getClueScrollAll().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollAll().getLevel()));
String easy = (result.getClueScrollEasy().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollEasy().getLevel()));
String medium = (result.getClueScrollMedium().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollMedium().getLevel()));
String hard = (result.getClueScrollHard().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollHard().getLevel()));
String elite = (result.getClueScrollElite().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollElite().getLevel()));
String master = (result.getClueScrollMaster().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollMaster().getLevel()));
text = "All clues: " + all + " | Rank: " + allRank + System.lineSeparator()
+ "Easy: " + easy + " | Rank: " + easyRank + System.lineSeparator()
+ "Medium: " + medium + " | Rank: " + mediumRank + System.lineSeparator()
+ "Hard: " + hard + " | Rank: " + hardRank + System.lineSeparator()
+ "Elite: " + elite + " | Rank: " + eliteRank + System.lineSeparator()
+ "Master: " + master + " | Rank: " + masterRank;
break;
}
case "Bounty Hunter - Rogue":
{
String rank = (result.getBountyHunterRogue().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getBountyHunterRogue().getRank());
text = "Bounty Hunter - Rogue Kills" + System.lineSeparator()
+ "Rank: " + rank;
break;
}
case "Bounty Hunter - Hunter":
{
String rank = (result.getBountyHunterHunter().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getBountyHunterHunter().getRank());
text = "Bounty Hunter - Hunter Kills" + System.lineSeparator()
+ "Rank: " + rank;
break;
}
case "Last Man Standing":
{
String rank = (result.getLastManStanding().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getLastManStanding().getRank());
text = "Last Man Standing" + System.lineSeparator()
+ "Rank: " + rank;
break;
}
case "Overall":
{
Skill requestedSkill = result.getSkill(skill);
String rank = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getRank());
String exp = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getExperience());
text = "Skill: " + skillName + System.lineSeparator()
+ "Rank: " + rank + System.lineSeparator()
+ "Experience: " + exp;
break;
}
default:
{
Skill requestedSkill = result.getSkill(skill);
String rank = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getRank());
String exp = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getExperience());
String remainingXp;
if (requestedSkill.getRank() == -1)
{
remainingXp = "Unranked";
}
else
{
int currentLevel = Experience.getLevelForXp((int) requestedSkill.getExperience());
int currentXp = (int) requestedSkill.getExperience();
int xpForCurrentLevel = Experience.getXpForLevel(currentLevel);
int xpForNextLevel = currentLevel + 1 <= Experience.MAX_VIRT_LEVEL ? Experience.getXpForLevel(currentLevel + 1) : -1;
c.gridx = 0;
c.gridy = 2;
gridBag.setConstraints(statsPanel, c);
add(statsPanel);
remainingXp = xpForNextLevel != -1 ? StackFormatter.formatNumber(xpForNextLevel - currentXp) : "0";
JPanel totalPanel = new JPanel();
totalPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
totalPanel.setLayout(new GridLayout(1, 2));
double xpGained = currentXp - xpForCurrentLevel;
double xpGoal = xpForNextLevel != -1 ? xpForNextLevel - xpForCurrentLevel : 100;
progress = (int) ((xpGained / xpGoal) * 100f);
totalPanel.add(makeSkillPanel(null)); //combat has no hiscore skill, refered to as null
totalPanel.add(makeSkillPanel(OVERALL));
}
text = "Skill: " + skillName + System.lineSeparator()
+ "Rank: " + rank + System.lineSeparator()
+ "Experience: " + exp + System.lineSeparator()
+ "Remaining XP: " + remainingXp;
break;
}
}
c.gridx = 0;
c.gridy = 3;
gridBag.setConstraints(totalPanel, c);
add(totalPanel);
details.setFont(UIManager.getFont("Label.font"));
details.setText(text);
JPanel minigamePanel = new JPanel();
// These aren't all on one row because when there's a label with four or more digits it causes the details
// panel to change its size for some reason...
minigamePanel.setLayout(new GridLayout(2, 3));
minigamePanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
if (progress >= 0)
{
progressBar.setVisible(true);
progressBar.setValue(progress);
progressBar.setBackground(Color.getHSBColor((progress / 100.f) * (120.f / 360.f), 1, 1));
}
else
{
progressBar.setVisible(false);
}
minigamePanel.add(makeSkillPanel(CLUE_SCROLL_ALL));
minigamePanel.add(makeSkillPanel(LAST_MAN_STANDING));
minigamePanel.add(makeSkillPanel(BOUNTY_HUNTER_ROGUE));
minigamePanel.add(makeSkillPanel(BOUNTY_HUNTER_HUNTER));
c.gridx = 0;
c.gridy = 4;
gridBag.setConstraints(minigamePanel, c);
add(minigamePanel);
}
@Override
@@ -492,16 +326,14 @@ public class HiscorePanel extends PluginPanel
input.requestFocusInWindow();
}
private JPanel makeSkillPanel(String skillName, HiscoreSkill skill)
/* Builds a JPanel displaying an icon and level/number associated with it */
private JPanel makeSkillPanel(HiscoreSkill skill)
{
JLabel label = new JLabel();
label.setFont(FontManager.getRunescapeSmallFont());
label.setText("--");
// Store the skill that the label displays so we can tell them apart
label.putClientProperty(SKILL_NAME, skillName);
label.putClientProperty(SKILL, skill);
String skillIcon = "skill_icons_small/" + skillName.toLowerCase() + ".png";
String skillIcon = "skill_icons_small/" + (skill == null ? "combat" : skill.getName().toLowerCase()) + ".png";
log.debug("Loading skill icon from {}", skillIcon);
try
@@ -518,36 +350,31 @@ public class HiscorePanel extends PluginPanel
log.warn(null, ex);
}
// Show skill details on hover
label.addMouseListener(new MouseInputAdapter()
{
@Override
public void mouseEntered(MouseEvent e)
{
JLabel source = (JLabel) e.getSource();
String skillName = (String) source.getClientProperty(SKILL_NAME);
HiscoreSkill skill = (HiscoreSkill) label.getClientProperty(SKILL);
changeDetail(skillName, skill);
}
});
skillLabels.add(label);
boolean totalLabel = skill == HiscoreSkill.OVERALL || skill == null; //overall or combat
label.setIconTextGap(totalLabel ? 10 : 4);
JPanel skillPanel = new JPanel();
skillPanel.setOpaque(false);
skillPanel.setBorder(new EmptyBorder(2, 0, 2, 0));
skillLabels.add(label);
skillPanel.add(skillLabels.get(skillLabels.size() - 1));
return skillPanel;
}
public void lookup(String username)
{
input.setText(username);
selectedEndPoint = HiscoreEndpoint.NORMAL; //reset the endpoint to regular player
updateButtons();
lookup();
}
private void lookup()
{
String lookup = input.getText();
details.setText("Loading...");
progressBar.setVisible(false);
lookup = sanitize(lookup);
@@ -556,43 +383,77 @@ public class HiscorePanel extends PluginPanel
return;
}
/* Runescape usernames can't be longer than 12 characters long */
if (lookup.length() > MAX_USERNAME_LENGTH)
{
input.setIcon(ERROR_ICON);
loading = false;
return;
}
input.setEditable(false);
input.setIcon(LOADING_ICON);
loading = true;
for (JLabel label : skillLabels)
{
label.setText("--");
}
// if for some reason no endpoint was selected, default to normal
if (selectedEndPoint == null)
{
selectedEndPoint = HiscoreEndpoint.NORMAL;
}
try
{
HiscoreEndpoint endpoint = HiscoreEndpoint.valueOf(endpointButtonGroup.getSelection().getActionCommand());
log.debug("Hiscore endpoint " + endpoint.name() + " selected");
result = hiscoreClient.lookup(lookup, endpoint);
log.debug("Hiscore endpoint " + selectedEndPoint.name() + " selected");
result = hiscoreClient.lookup(lookup, selectedEndPoint);
}
catch (IOException ex)
{
log.warn("Error fetching Hiscore data " + ex.getMessage());
details.setText("Error fetching Hiscore data");
progressBar.setVisible(false);
input.setIcon(ERROR_ICON);
input.setEditable(true);
loading = false;
return;
}
/*
For some reason, the fetch results would sometimes return a not null object
with all null attributes, to check for that, i'll just null check one of the attributes.
*/
if (result.getAttack() == null)
{
input.setIcon(ERROR_ICON);
input.setEditable(true);
loading = false;
return;
}
//successful player search
input.setIcon(SEARCH_ICON);
input.setEditable(true);
loading = false;
int index = 0;
for (JLabel label : skillLabels)
{
String skillName = (String) label.getClientProperty(SKILL_NAME);
HiscoreSkill skill = (HiscoreSkill) label.getClientProperty(SKILL);
HiscoreSkill skill = find(index);
if (skillName.equals("Combat"))
if (skill == null)
{
if (result.getPlayer() != null)
{
int combatLevel = Experience.getCombatLevel(
result.getAttack().getLevel(),
result.getStrength().getLevel(),
result.getDefence().getLevel(),
result.getHitpoints().getLevel(),
result.getMagic().getLevel(),
result.getRanged().getLevel(),
result.getPrayer().getLevel()
result.getAttack().getLevel(),
result.getStrength().getLevel(),
result.getDefence().getLevel(),
result.getHitpoints().getLevel(),
result.getMagic().getLevel(),
result.getRanged().getLevel(),
result.getPrayer().getLevel()
);
label.setText(Integer.toString(combatLevel));
}
@@ -600,7 +461,6 @@ public class HiscorePanel extends PluginPanel
else if (result.getSkill(skill) != null && result.getSkill(skill).getRank() != -1)
{
Skill s = result.getSkill(skill);
int level;
if (config.virtualLevels() && SKILLS.contains(skill))
{
@@ -613,12 +473,191 @@ public class HiscorePanel extends PluginPanel
label.setText(Integer.toString(level));
}
label.setToolTipText(detailsHtml(skill));
index++;
}
}
void addInputKeyListener(KeyListener l)
{
this.input.addInputKeyListener(l);
}
void removeInputKeyListener(KeyListener l)
{
this.input.removeInputKeyListener(l);
}
/*
Returns a hiscore skill based on it's display order.
*/
private HiscoreSkill find(int index)
{
if (index < SKILLS.size())
{
return SKILLS.get(index);
}
// Clear details panel
details.setFont(UIManager.getFont("Label.font").deriveFont(Font.ITALIC));
details.setText("Hover over a skill for details");
progressBar.setVisible(false);
switch (index - SKILLS.size())
{
case 0:
return null;
case 1:
return HiscoreSkill.OVERALL;
case 2:
return HiscoreSkill.CLUE_SCROLL_ALL;
case 3:
return HiscoreSkill.LAST_MAN_STANDING;
case 4:
return HiscoreSkill.BOUNTY_HUNTER_ROGUE;
case 5:
return HiscoreSkill.BOUNTY_HUNTER_HUNTER;
}
return null;
}
/*
Builds a html string to display on tooltip (when hovering a skill).
*/
private String detailsHtml(HiscoreSkill skill)
{
String openingTags = "<html><body style = 'padding: 5px;color:#989898'>";
String closingTags = "</html><body>";
String content = "";
if (result != null)
{
if (skill == null)
{
double combatLevel = Experience.getCombatLevelPrecise(
result.getAttack().getLevel(),
result.getStrength().getLevel(),
result.getDefence().getLevel(),
result.getHitpoints().getLevel(),
result.getMagic().getLevel(),
result.getRanged().getLevel(),
result.getPrayer().getLevel()
);
double combatExperience = result.getAttack().getExperience()
+ result.getStrength().getExperience() + result.getDefence().getExperience()
+ result.getHitpoints().getExperience() + result.getMagic().getExperience()
+ result.getRanged().getExperience() + result.getPrayer().getExperience();
content += "<p><span style = 'color:white'>Skill:</span> Combat</p>";
content += "<p><span style = 'color:white'>Exact Combat Level:</span> " + StackFormatter.formatNumber(combatLevel) + "</p>";
content += "<p><span style = 'color:white'>Experience:</span> " + StackFormatter.formatNumber(combatExperience) + "</p>";
}
else
{
switch (skill)
{
case CLUE_SCROLL_ALL:
{
String rank = (result.getClueScrollAll().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollAll().getRank());
String allRank = (result.getClueScrollAll().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollAll().getRank());
String easyRank = (result.getClueScrollEasy().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollEasy().getRank());
String mediumRank = (result.getClueScrollMedium().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollMedium().getRank());
String hardRank = (result.getClueScrollHard().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollHard().getRank());
String eliteRank = (result.getClueScrollElite().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollElite().getRank());
String masterRank = (result.getClueScrollMaster().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollMaster().getRank());
String all = (result.getClueScrollAll().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollAll().getLevel()));
String easy = (result.getClueScrollEasy().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollEasy().getLevel()));
String medium = (result.getClueScrollMedium().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollMedium().getLevel()));
String hard = (result.getClueScrollHard().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollHard().getLevel()));
String elite = (result.getClueScrollElite().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollElite().getLevel()));
String master = (result.getClueScrollMaster().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollMaster().getLevel()));
content += "<p><span style = 'color:white'>All:</span> " + all + " <span style = 'color:white'>Rank:</span> " + allRank + "</p>";
content += "<p><span style = 'color:white'>Easy:</span> " + easy + " <span style = 'color:white'>Rank:</span> " + easyRank + "</p>";
content += "<p><span style = 'color:white'>Medium:</span> " + medium + " <span style = 'color:white'>Rank:</span> " + mediumRank + "</p>";
content += "<p><span style = 'color:white'>Hard:</span> " + hard + " <span style = 'color:white'>Rank:</span> " + hardRank + "</p>";
content += "<p><span style = 'color:white'>Elite:</span> " + elite + " <span style = 'color:white'>Rank:</span> " + eliteRank + "</p>";
content += "<p><span style = 'color:white'>Master:</span> " + master + " <span style = 'color:white'>Rank:</span> " + masterRank + "</p>";
break;
}
case BOUNTY_HUNTER_ROGUE:
{
String rank = (result.getBountyHunterRogue().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getBountyHunterRogue().getRank());
content += "<p><span style = 'color:white'>Rank:</span> " + rank + "</p>";
break;
}
case BOUNTY_HUNTER_HUNTER:
{
String rank = (result.getBountyHunterHunter().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getBountyHunterHunter().getRank());
content += "<p><span style = 'color:white'>Rank:</span> " + rank + "</p>";
break;
}
case LAST_MAN_STANDING:
{
String rank = (result.getLastManStanding().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getLastManStanding().getRank());
content += "<p><span style = 'color:white'>Rank:</span> " + rank + "</p>";
break;
}
case OVERALL:
{
Skill requestedSkill = result.getSkill(skill);
String rank = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getRank());
String exp = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getExperience());
content += "<p><span style = 'color:white'>Skill:</span> " + skill.getName() + "</p>";
content += "<p><span style = 'color:white'>Rank:</span> " + rank + "</p>";
content += "<p><span style = 'color:white'>Experience:</span> " + exp + "</p>";
break;
}
default:
{
Skill requestedSkill = result.getSkill(skill);
String rank = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getRank());
String exp = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getExperience());
String remainingXp;
if (requestedSkill.getRank() == -1)
{
remainingXp = "Unranked";
}
else
{
int currentLevel = Experience.getLevelForXp((int) requestedSkill.getExperience());
remainingXp = (currentLevel + 1 <= Experience.MAX_VIRT_LEVEL) ? StackFormatter.formatNumber(Experience.getXpForLevel(currentLevel + 1) - requestedSkill.getExperience()) : "0";
}
content += "<p><span style = 'color:white'>Skill:</span> " + skill.getName() + "</p>";
content += "<p><span style = 'color:white'>Rank:</span> " + rank + "</p>";
content += "<p><span style = 'color:white'>Experience:</span> " + exp + "</p>";
content += "<p><span style = 'color:white'>Remaining XP:</span> " + remainingXp + "</p>";
break;
}
}
}
}
/**
* Adds a html progress bar to the hover information
*/
if (SKILLS.contains(skill))
{
int currentLevel = Experience.getLevelForXp((int) result.getSkill(skill).getExperience());
int currentXp = (int) result.getSkill(skill).getExperience();
int xpForCurrentLevel = Experience.getXpForLevel(currentLevel);
int xpForNextLevel = currentLevel + 1 <= Experience.MAX_VIRT_LEVEL ? Experience.getXpForLevel(currentLevel + 1) : -1;
double xpGained = currentXp - xpForCurrentLevel;
double xpGoal = xpForNextLevel != -1 ? xpForNextLevel - xpForCurrentLevel : 100;
int progress = (int) ((xpGained / xpGoal) * 100f);
// had to wrap the bar with an empty div, if i added the margin directly to the bar, it would mess up
content += "<div style = 'margin-top:3px'>"
+ "<div style = 'background: #070707; border: 1px solid #070707; height: 6px; width: 100%;'>"
+ "<div style = 'height: 6px; width: " + progress + "%; background: #dc8a00;'>"
+ "</div>"
+ "</div>"
+ "</div>";
}
return openingTags + content + closingTags;
}
private static String sanitize(String lookup)
@@ -626,20 +665,18 @@ public class HiscorePanel extends PluginPanel
return lookup.replace('\u00A0', ' ');
}
/*
When an endpoint gets selected, this method will correctly display the selected one
with an orange underline.
*/
private void updateButtons()
{
for (JToggleButton button : endpointButtons)
for (JPanel panel : endPoints)
{
Color color;
if (button.isSelected())
{
color = Color.CYAN;
}
else
{
color = Color.WHITE;
}
button.setBackground(color);
panel.setBorder(new EmptyBorder(0, 0, 1, 0));
}
int selectedIndex = selectedEndPoint.ordinal();
endPoints.get(selectedIndex).setBorder(new MatteBorder(0, 0, 1, 0, ColorScheme.BRAND_ORANGE));
}
}
}

View File

@@ -98,12 +98,13 @@ public class HiscorePlugin extends Plugin
BufferedImage icon;
synchronized (ImageIO.class)
{
icon = ImageIO.read(getClass().getResourceAsStream("hiscore.gif"));
icon = ImageIO.read(getClass().getResourceAsStream("normal.png"));
}
navButton = NavigationButton.builder()
.name("Hiscore")
.icon(icon)
.priority(5)
.panel(hiscorePanel)
.build();

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 Abex
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,14 +28,22 @@ package net.runelite.client.plugins.info;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import javax.inject.Singleton;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.LayoutStyle;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.HyperlinkEvent;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
@@ -42,8 +51,10 @@ import net.runelite.api.events.SessionClose;
import net.runelite.api.events.SessionOpen;
import net.runelite.client.RuneLiteProperties;
import net.runelite.client.account.SessionManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.util.LinkBrowser;
import net.runelite.client.util.RunnableExceptionLogger;
@Slf4j
@@ -52,6 +63,14 @@ public class InfoPanel extends PluginPanel
{
private static final String RUNELITE_LOGIN = "https://runelite_login/";
private static final ImageIcon ARROW_RIGHT_ICON;
private static final ImageIcon GITHUB_ICON;
private static final ImageIcon DISCORD_ICON;
private static final ImageIcon PATREON_ICON;
private final JLabel loggedLabel = new JLabel();
private final JRichTextPane emailLabel = new JRichTextPane();
@Inject
@Nullable
private Client client;
@@ -68,33 +87,58 @@ public class InfoPanel extends PluginPanel
@Inject
private ScheduledExecutorService executor;
private final GroupLayout layout = new GroupLayout(this);
private final JLabel usernameHeader = new JLabel();
private final JRichTextPane username = new JRichTextPane();
static
{
try
{
synchronized (ImageIO.class)
{
ARROW_RIGHT_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("arrow_right.png")));
GITHUB_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("github_icon.png")));
DISCORD_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("discord_icon.png")));
PATREON_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("patreon_icon.png")));
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
void init()
{
setLayout(layout);
setLayout(new BorderLayout());
setBackground(ColorScheme.DARK_GRAY_COLOR);
setBorder(new EmptyBorder(10, 10, 10, 10));
JPanel versionPanel = new JPanel();
versionPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
versionPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
versionPanel.setLayout(new GridLayout(0, 1));
final Font smallFont = FontManager.getRunescapeSmallFont();
final JLabel runeliteVersionHeader = new JLabel("RuneLite version");
runeliteVersionHeader.setFont(smallFont);
final JLabel runeliteVersion = new JLabel(runeLiteProperties.getVersion());
JLabel version = new JLabel(htmlLabel("RuneLite version: ", runeLiteProperties.getVersion()));
version.setFont(smallFont);
final JLabel runescapeVersionHeader = new JLabel("OldSchool Engine");
runescapeVersionHeader.setFont(smallFont);
JLabel revision = new JLabel();
revision.setFont(smallFont);
String engineVer = "Unknown";
if (client != null)
{
engineVer = String.format("Rev %s", runeLiteProperties.getRunescapeVersion());
}
final JLabel runescapeVersion = new JLabel(engineVer);
usernameHeader.setFont(smallFont);
username.enableAutoLinkHandler(false);
username.addHyperlinkListener(e ->
revision.setText(htmlLabel("Oldschool revision: ", engineVer));
loggedLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
loggedLabel.setFont(smallFont);
emailLabel.setForeground(Color.WHITE);
emailLabel.setFont(smallFont);
emailLabel.enableAutoLinkHandler(false);
emailLabel.addHyperlinkListener(e ->
{
if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType()) && e.getURL() != null)
{
@@ -105,51 +149,74 @@ public class InfoPanel extends PluginPanel
}
});
versionPanel.add(version);
versionPanel.add(revision);
versionPanel.add(Box.createGlue());
versionPanel.add(loggedLabel);
versionPanel.add(emailLabel);
updateLoggedIn();
final JRichTextPane issueLink = new JRichTextPane("text/html",
"RuneLite is open source!<br>"
+ "Found an issue? Want a feature?<br>"
+ "<a href=\"https://github.com/runelite/runelite/issues\">"
+ "Open an issue on GitHub!"
+ "</a>"
);
JPanel actionsContainer = new JPanel();
actionsContainer.setBorder(new EmptyBorder(10, 0, 0, 0));
actionsContainer.setOpaque(false);
actionsContainer.setLayout(new GridLayout(3, 1, 0, 10));
setBorder(BorderFactory.createEmptyBorder(2, 6, 6, 6));
actionsContainer.add(buildLinkPanel(GITHUB_ICON, "Report an issue or", "make a suggestion", runeLiteProperties.getGithubLink()));
actionsContainer.add(buildLinkPanel(DISCORD_ICON, "Talk to us on our", "discord server", runeLiteProperties.getDiscordInvite()));
actionsContainer.add(buildLinkPanel(PATREON_ICON, "Become a patron to", "help support RuneLite", runeLiteProperties.getPatreonLink()));
layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup()
.addComponent(runeliteVersionHeader)
.addComponent(runescapeVersionHeader)
).addGroup(layout.createParallelGroup()
.addComponent(runeliteVersion)
.addComponent(runescapeVersion)
).addGap(6)
.addComponent(usernameHeader)
.addGroup(layout.createParallelGroup()
.addComponent(username)
)
.addGap(12)
.addComponent(issueLink)
);
layout.setHorizontalGroup(layout.createParallelGroup()
.addGroup(layout.createSequentialGroup()
.addComponent(runeliteVersionHeader)
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
.addComponent(runescapeVersionHeader))
.addGroup(layout.createSequentialGroup()
.addComponent(runeliteVersion)
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
.addComponent(runescapeVersion))
.addComponent(usernameHeader)
.addComponent(username)
.addComponent(issueLink)
);
add(versionPanel, BorderLayout.NORTH);
add(actionsContainer, BorderLayout.CENTER);
eventBus.register(this);
}
/**
* Builds a link panel with a given icon, text and url to redirect to.
*/
private static JPanel buildLinkPanel(ImageIcon icon, String topText, String bottomText, String url)
{
JPanel container = new JPanel();
container.setBackground(ColorScheme.DARKER_GRAY_COLOR);
container.setLayout(new BorderLayout());
container.setBorder(new EmptyBorder(10, 10, 10, 10));
container.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent mouseEvent)
{
LinkBrowser.browse(url);
}
});
JLabel iconLabel = new JLabel(icon);
container.add(iconLabel, BorderLayout.WEST);
JPanel textContainer = new JPanel();
textContainer.setOpaque(false);
textContainer.setLayout(new GridLayout(2, 1));
textContainer.setBorder(new EmptyBorder(5, 10, 5, 10));
JLabel topLine = new JLabel(topText);
topLine.setForeground(Color.WHITE);
topLine.setFont(FontManager.getRunescapeSmallFont());
JLabel bottomLine = new JLabel(bottomText);
bottomLine.setForeground(Color.WHITE);
bottomLine.setFont(FontManager.getRunescapeSmallFont());
textContainer.add(topLine);
textContainer.add(bottomLine);
container.add(textContainer, BorderLayout.CENTER);
JLabel arrowLabel = new JLabel(ARROW_RIGHT_ICON);
container.add(arrowLabel, BorderLayout.EAST);
return container;
}
private void updateLoggedIn()
{
final String name = sessionManager.getAccountSession() != null
@@ -158,18 +225,23 @@ public class InfoPanel extends PluginPanel
if (name != null)
{
username.setContentType("text/plain");
username.setText(name);
usernameHeader.setText("Logged in as");
emailLabel.setContentType("text/plain");
emailLabel.setText(name);
loggedLabel.setText("Logged in as");
}
else
{
username.setContentType("text/html");
username.setText("<a href=\"" + RUNELITE_LOGIN + "\">Login</a> to sync settings to the cloud.");
usernameHeader.setText("Not logged in");
emailLabel.setContentType("text/html");
emailLabel.setText("<a href=\"" + RUNELITE_LOGIN + "\">Login</a> to sync settings to the cloud.");
loggedLabel.setText("Not logged in");
}
}
private static String htmlLabel(String key, String value)
{
return "<html><body style = 'color:#a5a5a5'>" + key + "<span style = 'color:white'>" + value + "</span></body></html>";
}
@Subscribe
public void onSessionOpen(SessionOpen sessionOpen)
{

View File

@@ -58,6 +58,7 @@ public class InfoPlugin extends Plugin
navButton = NavigationButton.builder()
.name("Info")
.icon(icon)
.priority(9)
.panel(panel)
.build();

View File

@@ -28,6 +28,7 @@ import java.awt.Color;
import javax.swing.GroupLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import net.runelite.client.ui.FontManager;
class BookPanel extends JPanel
@@ -36,6 +37,9 @@ class BookPanel extends JPanel
BookPanel(Book b)
{
setBorder(new EmptyBorder(3, 3, 3, 3));
setOpaque(false);
GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
@@ -74,4 +78,4 @@ class BookPanel extends JPanel
{
location.setForeground(target ? Color.GREEN : Color.WHITE);
}
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 Abex
* Copyright (c) 2018 Psikoi
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,35 +26,71 @@
package net.runelite.client.plugins.kourendlibrary;
import com.google.inject.Inject;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.imageio.ImageIO;
import javax.inject.Singleton;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.util.SwingUtil;
@Singleton
public class KourendLibraryPanel extends PluginPanel
{
private static final ImageIcon RESET_ICON;
private static final ImageIcon RESET_CLICK_ICON;
@Inject
private Library library;
private final HashMap<Book, BookPanel> bookPanels = new HashMap<>();
static
{
try
{
synchronized (ImageIO.class)
{
BufferedImage resetIcon = ImageIO.read(KourendLibraryPanel.class.getResourceAsStream("reset.png"));
RESET_ICON = new ImageIcon(resetIcon);
RESET_CLICK_ICON = new ImageIcon(SwingUtil.grayscaleOffset(resetIcon, -100));
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
void init()
{
GroupLayout layout = new GroupLayout(this);
setLayout(layout);
setBorder(new EmptyBorder(10, 10, 10, 10));
setBackground(ColorScheme.DARK_GRAY_COLOR);
JPanel books = new JPanel(new GridBagLayout());
books.setOpaque(false);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
@@ -70,21 +107,45 @@ public class KourendLibraryPanel extends PluginPanel
c.gridy++;
});
JButton reset = new JButton("Reset");
reset.addActionListener(e ->
JLabel reset = new JLabel(RESET_ICON);
reset.addMouseListener(new MouseAdapter()
{
library.reset();
update();
@Override
public void mousePressed(MouseEvent mouseEvent)
{
reset.setIcon(RESET_CLICK_ICON);
library.reset();
update();
}
@Override
public void mouseReleased(MouseEvent mouseEvent)
{
reset.setIcon(RESET_ICON);
}
});
JPanel header = new JPanel();
header.setOpaque(false);
header.setLayout(new BorderLayout());
header.setBorder(new CompoundBorder(
BorderFactory.createMatteBorder(0, 0, 1, 0, new Color(58, 58, 58)),
BorderFactory.createEmptyBorder(0, 0, 10, 0)));
JLabel pluginName = new JLabel("Kourend Library Plugin");
pluginName.setForeground(Color.WHITE);
header.add(reset, BorderLayout.EAST);
header.add(pluginName, BorderLayout.CENTER);
layout.setHorizontalGroup(layout.createParallelGroup()
.addComponent(books)
.addComponent(reset)
.addComponent(header)
);
layout.setVerticalGroup(layout.createSequentialGroup()
.addComponent(header)
.addGap(10)
.addComponent(books)
.addGap(4)
.addComponent(reset)
);
update();
@@ -134,4 +195,4 @@ public class KourendLibraryPanel extends PluginPanel
}
});
}
}
}

View File

@@ -94,6 +94,7 @@ public class KourendLibraryPlugin extends Plugin
navButton = NavigationButton.builder()
.name("Kourend Library")
.priority(6)
.icon(icon)
.panel(panel)
.build();

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 Charlie Waters
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,13 +25,17 @@
*/
package net.runelite.client.plugins.notes;
import java.awt.Color;
import javax.swing.BorderFactory;
import javax.swing.JTextArea;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
import java.awt.BorderLayout;
@@ -50,14 +55,24 @@ public class NotesPanel extends PluginPanel
getParent().add(this, BorderLayout.CENTER);
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(2, 6, 6, 6));
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
setBackground(ColorScheme.DARK_GRAY_COLOR);
final JLabel notesHeader = new JLabel("Notes");
notesHeader.setForeground(Color.WHITE);
notesHeader.setBorder(new EmptyBorder(1, 0, 10, 0));
add(notesHeader, BorderLayout.NORTH);
notesEditor.setLineWrap(true);
notesEditor.setWrapStyleWord(true);
JPanel notesContainer = new JPanel();
notesContainer.setLayout(new BorderLayout());
notesContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
notesEditor.setOpaque(false);
// load note text
String data = config.notesData();
notesEditor.setText(data);
@@ -90,7 +105,10 @@ public class NotesPanel extends PluginPanel
}
}
});
add(notesEditor, BorderLayout.CENTER);
notesContainer.add(notesEditor, BorderLayout.CENTER);
notesContainer.setBorder(new EmptyBorder(10, 10, 10, 10));
add(notesContainer, BorderLayout.CENTER);
}
void setNotes(String data)

View File

@@ -74,6 +74,7 @@ public class NotesPlugin extends Plugin
navButton = NavigationButton.builder()
.name("Notes")
.icon(icon)
.priority(7)
.panel(panel)
.build();

View File

@@ -38,6 +38,7 @@ import javax.swing.JButton;
import javax.swing.JPanel;
import net.runelite.api.Client;
import net.runelite.client.game.SkillIconManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
class SkillCalculatorPanel extends PluginPanel
@@ -64,10 +65,13 @@ class SkillCalculatorPanel extends PluginPanel
uiButtonGridConstraints.insets = new Insets(2, 2, 2, 2);
uiButtonGrid.setLayout(uiButtonGridLayout);
uiButtonGrid.setBackground(ColorScheme.DARK_GRAY_COLOR);
uiButtonGrid.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
addCalculatorButtons();
final UICalculatorInputArea uiInput = new UICalculatorInputArea();
uiInput.setBackground(ColorScheme.DARK_GRAY_COLOR);
uiCalculator = new SkillCalculator(client, uiInput);
add(uiButtonGrid);

View File

@@ -75,7 +75,12 @@ public class SkillCalculatorPlugin extends Plugin
SkillCalculator.itemManager = itemManager;
uiPanel = new SkillCalculatorPanel(skillIconManager, client);
uiNavigationButton = NavigationButton.builder().name("Skill Calculator").icon(icon).panel(uiPanel).build();
uiNavigationButton = NavigationButton.builder()
.name("Skill Calculator")
.icon(icon)
.priority(6)
.panel(uiPanel)
.build();
pluginToolbar.addNavigation(uiNavigationButton);
}

View File

@@ -34,7 +34,7 @@ import javax.swing.JLabel;
import javax.swing.JPanel;
import net.runelite.client.plugins.skillcalculator.beans.SkillDataEntry;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.JShadowedLabel;
import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
class UIActionSlot extends JPanel
{

View File

@@ -33,7 +33,7 @@ import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.JShadowedLabel;
import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
class UICombinedActionSlot extends JPanel
{

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2018, Psikoi <https://github.com/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.xptracker;
import java.awt.Color;
import lombok.Getter;
public enum SkillColor
{
ATTACK(105, 32, 7),
DEFENCE(98, 119, 190),
STRENGTH(4, 149, 90),
HITPOINTS(131, 126, 126),
RANGED(109, 144, 23),
PRAYER(159, 147, 35),
MAGIC(50, 80, 193),
COOKING(112, 35, 134),
WOODCUTTING(52, 140, 37),
FLETCHING(3, 141, 125),
FISHING(106, 132, 164),
FIREMAKING(189, 120, 25),
CRAFTING(151, 110, 77),
SMITHING(108, 107, 82),
MINING(93, 143, 167),
HERBLORE(7, 133, 9),
AGILITY(58, 60, 137),
THIEVING(108, 52, 87),
SLAYER(100, 100, 100),
FARMING(101, 152, 63),
RUNECRAFT(170, 141, 26),
HUNTER(92, 89, 65),
CONSTRUCTION(130, 116, 95);
@Getter
private final Color color;
SkillColor(int red, int green, int blue)
{
this.color = new Color(red, green, blue);
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,54 +27,54 @@ package net.runelite.client.plugins.xptracker;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.awt.image.LookupOp;
import java.awt.image.LookupTable;
import java.io.IOException;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.Skill;
import net.runelite.client.game.SkillIconManager;
import net.runelite.client.ui.JShadowedLabel;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.components.ProgressBar;
import net.runelite.client.util.LinkBrowser;
import org.pushingpixels.substance.internal.SubstanceSynapse;
import net.runelite.client.util.StackFormatter;
import net.runelite.client.util.SwingUtil;
@Slf4j
class XpInfoBox extends JPanel
{
private static final Rectangle ICON_BOUNDS = new Rectangle(0, 0, 26, 26);
private final Client client;
private final JPanel panel;
@Getter(AccessLevel.PACKAGE)
private final Skill skill;
/* The tracker's wrapping container */
private final JPanel container = new JPanel();
/* Contains the skill icon and the stats panel */
private final JPanel headerPanel = new JPanel();
/* Contains all the skill information (exp gained, per hour, etc) */
private final JPanel statsPanel = new JPanel();
private final JProgressBar progressBar = new JProgressBar();
private final JLabel xpHr = new JLabel();
private final JLabel xpGained = new JLabel();
private final JLabel xpLeft = new JLabel();
private final ProgressBar progressBar = new ProgressBar();
private final JLabel expGained = new JLabel();
private final JLabel expHour = new JLabel();
private final JLabel expLeft = new JLabel();
private final JLabel actionsLeft = new JLabel();
private final JLabel levelLabel = new JShadowedLabel();
XpInfoBox(XpTrackerPlugin xpTrackerPlugin, Client client, JPanel panel, Skill skill, SkillIconManager iconManager) throws IOException
{
@@ -82,121 +83,81 @@ class XpInfoBox extends JPanel
this.skill = skill;
setLayout(new BorderLayout());
setBorder(new CompoundBorder
(
new EmptyBorder(2, 0, 2, 0),
new LineBorder(getBackground().brighter(), 1)
));
// Expand stats panel on click
Color backgroundColor = getBackground();
MouseListener panelMouseListener = new MouseAdapter()
{
@Override
public void mouseEntered(MouseEvent e)
{
container.setBackground(backgroundColor.darker().darker());
}
@Override
public void mouseExited(MouseEvent e)
{
container.setBackground(backgroundColor);
}
@Override
public void mouseReleased(MouseEvent e)
{
if (SwingUtilities.isLeftMouseButton(e))
{
showStatsPanel();
}
}
};
setBorder(new EmptyBorder(10, 0, 0, 0));
setOpaque(false);
container.setLayout(new BorderLayout());
container.setBorder(new EmptyBorder(5, 5, 5, 5));
container.addMouseListener(panelMouseListener);
container.setOpaque(true);
container.setBackground(ColorScheme.DARKER_GRAY_COLOR);
// Create open xp tracker menu
final JMenuItem openXpTracker = new JMenuItem("Open XP tracker");
final JMenuItem openXpTracker = new JMenuItem("Open online tracker");
openXpTracker.addActionListener(e -> LinkBrowser.browse(XpPanel.buildXpTrackerUrl(client.getLocalPlayer(), skill)));
// Create reset menu
final JMenuItem reset = new JMenuItem("Reset");
reset.addActionListener(e -> reset());
// Create popup menu
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
popupMenu.add(openXpTracker);
popupMenu.add(reset);
container.setComponentPopupMenu(popupMenu);
// Create icon panel
final JPanel iconBarPanel = new JPanel();
iconBarPanel.setLayout(new BorderLayout(5, 0));
iconBarPanel.setOpaque(false);
JLabel skillIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(skill)));
skillIcon.setHorizontalAlignment(SwingConstants.CENTER);
skillIcon.setVerticalAlignment(SwingConstants.CENTER);
skillIcon.setPreferredSize(new Dimension(35, 35));
// Create skill/reset icon
final BufferedImage skillImage = iconManager.getSkillImage(skill);
final JButton skillIcon = new JButton();
headerPanel.setOpaque(false);
headerPanel.setLayout(new BorderLayout());
skillIcon.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE);
skillIcon.putClientProperty(SubstanceSynapse.BUTTON_NEVER_PAINT_BACKGROUND, Boolean.TRUE);
skillIcon.setIcon(new ImageIcon(skillImage));
skillIcon.setRolloverIcon(new ImageIcon(createHoverImage(skillImage)));
skillIcon.setToolTipText("Reset " + skill.getName() + " tracker");
skillIcon.addActionListener(e -> xpTrackerPlugin.resetSkillState(skill));
skillIcon.setBounds(ICON_BOUNDS);
skillIcon.setOpaque(false);
skillIcon.setFocusPainted(false);
// Create level label
levelLabel.setHorizontalAlignment(JLabel.CENTER);
levelLabel.setForeground(Color.YELLOW);
levelLabel.setBounds(ICON_BOUNDS);
levelLabel.setOpaque(false);
// Create pane for grouping skill icon and level label
final JLayeredPane layeredPane = new JLayeredPane();
layeredPane.add(skillIcon, new Integer(1));
layeredPane.add(levelLabel, new Integer(2));
layeredPane.setPreferredSize(ICON_BOUNDS.getSize());
iconBarPanel.add(layeredPane, BorderLayout.LINE_START);
// Create progress bar
progressBar.setStringPainted(true);
progressBar.setValue(0);
progressBar.setMinimum(0);
progressBar.setMaximum(100);
progressBar.setBackground(Color.RED);
progressBar.addMouseListener(panelMouseListener);
iconBarPanel.add(progressBar, BorderLayout.CENTER);
container.add(iconBarPanel, BorderLayout.NORTH);
// Stats panel
statsPanel.setLayout(new GridLayout(2, 2));
statsPanel.setBorder(new EmptyBorder(3, 0, 0, 0));
statsPanel.setLayout(new BorderLayout());
statsPanel.setBorder(new EmptyBorder(9, 5, 9, 10));
statsPanel.setOpaque(false);
statsPanel.add(xpGained);
xpHr.setHorizontalAlignment(SwingConstants.RIGHT);
statsPanel.add(xpHr);
statsPanel.add(xpLeft);
actionsLeft.setHorizontalAlignment(SwingConstants.RIGHT);
statsPanel.add(actionsLeft);
JPanel leftPanel = new JPanel();
leftPanel.setOpaque(false);
leftPanel.setLayout(new GridLayout(2, 1));
add(container, BorderLayout.CENTER);
}
expGained.setFont(FontManager.getRunescapeSmallFont());
expHour.setFont(FontManager.getRunescapeSmallFont());
private void showStatsPanel()
{
if (statsPanel.isShowing())
{
container.remove(statsPanel);
revalidate();
}
else
{
container.add(statsPanel, BorderLayout.SOUTH);
revalidate();
}
leftPanel.add(expGained);
leftPanel.add(expHour);
JPanel rightPanel = new JPanel();
rightPanel.setOpaque(false);
rightPanel.setLayout(new GridLayout(2, 1));
expLeft.setFont(FontManager.getRunescapeSmallFont());
actionsLeft.setFont(FontManager.getRunescapeSmallFont());
rightPanel.add(expLeft);
rightPanel.add(actionsLeft);
statsPanel.add(leftPanel, BorderLayout.WEST);
statsPanel.add(rightPanel, BorderLayout.EAST);
headerPanel.add(skillIcon, BorderLayout.WEST);
headerPanel.add(statsPanel, BorderLayout.CENTER);
JPanel progressWrapper = new JPanel();
progressWrapper.setOpaque(false);
progressWrapper.setLayout(new BorderLayout());
progressWrapper.setBorder(new EmptyBorder(0, 7, 7, 7));
progressBar.setMaximumValue(100);
progressBar.setBackground(new Color(61, 56, 49));
progressBar.setForeground(SkillColor.values()[skill.ordinal()].getColor());
progressWrapper.add(progressBar, BorderLayout.NORTH);
container.add(headerPanel, BorderLayout.NORTH);
container.add(progressWrapper, BorderLayout.SOUTH);
add(container, BorderLayout.NORTH);
}
void reset()
@@ -221,46 +182,46 @@ class XpInfoBox extends JPanel
panel.revalidate();
}
levelLabel.setText(String.valueOf(xpSnapshotSingle.getCurrentLevel()));
xpGained.setText(XpPanel.formatLine(xpSnapshotSingle.getXpGainedInSession(), "xp gained"));
xpLeft.setText(XpPanel.formatLine(xpSnapshotSingle.getXpRemainingToGoal(), "xp left"));
actionsLeft.setText(XpPanel.formatLine(xpSnapshotSingle.getActionsRemainingToGoal(), "actions left"));
// Update information labels
expGained.setText(htmlLabel("XP Gained: ", xpSnapshotSingle.getXpGainedInSession()));
expLeft.setText(htmlLabel("XP Left: ", xpSnapshotSingle.getXpRemainingToGoal()));
actionsLeft.setText(htmlLabel("Actions: ", xpSnapshotSingle.getActionsRemainingToGoal()));
final int progress = xpSnapshotSingle.getSkillProgressToGoal();
progressBar.setValue(progress);
progressBar.setBackground(Color.getHSBColor((progress / 100.f) * (120.f / 360.f), 1, 1));
// Update progress bar
progressBar.setValue(xpSnapshotSingle.getSkillProgressToGoal());
progressBar.setCenterLabel(xpSnapshotSingle.getSkillProgressToGoal() + "%");
progressBar.setLeftLabel("Lvl. " + xpSnapshotSingle.getCurrentLevel());
progressBar.setRightLabel("Lvl. " + (xpSnapshotSingle.getCurrentLevel() + 1));
progressBar.setToolTipText("<html>"
+ XpPanel.formatLine(xpSnapshotSingle.getActionsInSession(), "actions")
+ xpSnapshotSingle.getActionsInSession() + " actions done"
+ "<br/>"
+ XpPanel.formatLine(xpSnapshotSingle.getActionsPerHour(), "actions/hr")
+ xpSnapshotSingle.getActionsPerHour() + " actions/hr"
+ "<br/>"
+ xpSnapshotSingle.getTimeTillGoal() + " till next lvl"
+ "</html>");
progressBar.repaint();
}
// Always update xp/hr as time always changes
xpHr.setText(XpPanel.formatLine(xpSnapshotSingle.getXpPerHour(), "xp/hr"));
// Update exp per hour seperately, everytime (not only when there's an update)
expHour.setText(htmlLabel("XP/Hour: ", xpSnapshotSingle.getXpPerHour()));
}
private static BufferedImage createHoverImage(BufferedImage image)
public static String htmlLabel(String key, int value)
{
LookupTable lookup = new LookupTable(0, 4)
String valueStr = value + "";
if (value > 9999999 || value < -9999999)
{
@Override
public int[] lookupPixel(int[] src, int[] dest)
{
if (dest[3] != 0)
{
dest[3] = 60;
}
valueStr = "Lots!";
}
else
{
valueStr = StackFormatter.quantityToRSDecimalStack(value);
}
return dest;
}
};
LookupOp op = new LookupOp(lookup, new RenderingHints(null));
return op.filter(image, null);
return "<html><body style = 'color:" + SwingUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR) + "'>" + key + "<span style = 'color:white'>" + valueStr + "</span></body></html>";
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2017, Cameron <moberg@tuta.io>
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,64 +30,93 @@ import java.awt.GridLayout;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Actor;
import net.runelite.api.Client;
import net.runelite.api.Skill;
import net.runelite.client.game.SkillIconManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.PluginErrorPanel;
import net.runelite.client.util.LinkBrowser;
import net.runelite.client.util.StackFormatter;
import okhttp3.HttpUrl;
@Slf4j
class XpPanel extends PluginPanel
{
private final Map<Skill, XpInfoBox> infoBoxes = new HashMap<>();
private final JLabel totalXpGained = new JLabel();
private final JLabel totalXpHr = new JLabel();
private final JLabel overallExpGained = new JLabel(XpInfoBox.htmlLabel("Gained: ", 0));
private final JLabel overallExpHour = new JLabel(XpInfoBox.htmlLabel("Per hour: ", 0));
private final JPanel overallPanel = new JPanel();
/* This displays the "No exp gained" text */
private final PluginErrorPanel errorPanel = new PluginErrorPanel();
XpPanel(XpTrackerPlugin xpTrackerPlugin, Client client, SkillIconManager iconManager)
{
super();
setBorder(new EmptyBorder(10, 10, 10, 10));
setBackground(ColorScheme.DARK_GRAY_COLOR);
final JPanel layoutPanel = new JPanel();
layoutPanel.setLayout(new BorderLayout(0, 3));
layoutPanel.setOpaque(false);
layoutPanel.setLayout(new BorderLayout());
add(layoutPanel);
final JPanel totalPanel = new JPanel();
totalPanel.setLayout(new BorderLayout());
totalPanel.setBorder(BorderFactory.createLineBorder(getBackground().brighter(), 1, true));
overallPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
overallPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
overallPanel.setLayout(new BorderLayout());
overallPanel.setVisible(false); // this will only become visible when the player gets exp
final JPanel infoPanel = new JPanel();
infoPanel.setLayout(new GridLayout(4, 1));
infoPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
// Create open xp tracker menu
final JMenuItem openXpTracker = new JMenuItem("Open online tracker");
openXpTracker.addActionListener(e -> LinkBrowser.browse(XpPanel.buildXpTrackerUrl(client.getLocalPlayer(), Skill.OVERALL)));
final JButton resetButton = new JButton("Reset All");
resetButton.addActionListener(e -> xpTrackerPlugin.resetAndInitState());
// Create reset all menu
final JMenuItem reset = new JMenuItem("Reset All");
reset.addActionListener(e -> xpTrackerPlugin.resetAndInitState());
final JButton openTrackerButton = new JButton("Open XP tracker");
openTrackerButton.addActionListener(e -> LinkBrowser.browse(buildXpTrackerUrl(client.getLocalPlayer(), Skill.OVERALL)));
// Create popup menu
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
popupMenu.add(openXpTracker);
popupMenu.add(reset);
overallPanel.setComponentPopupMenu(popupMenu);
totalXpGained.setText(formatLine(0, "total xp gained"));
totalXpHr.setText(formatLine(0, "total xp/hr"));
final JLabel overallIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(Skill.OVERALL)));
final JPanel overallInfo = new JPanel();
overallInfo.setOpaque(false);
overallInfo.setLayout(new GridLayout(2, 1));
overallInfo.setBorder(new EmptyBorder(0, 10, 0, 0));
overallExpGained.setFont(FontManager.getRunescapeSmallFont());
overallExpHour.setFont(FontManager.getRunescapeSmallFont());
overallInfo.add(overallExpGained);
overallInfo.add(overallExpHour);
overallPanel.add(overallIcon, BorderLayout.WEST);
overallPanel.add(overallInfo, BorderLayout.CENTER);
infoPanel.add(totalXpGained);
infoPanel.add(totalXpHr);
infoPanel.add(resetButton);
infoPanel.add(openTrackerButton);
totalPanel.add(infoPanel, BorderLayout.CENTER);
layoutPanel.add(totalPanel, BorderLayout.NORTH);
final JPanel infoBoxPanel = new JPanel();
infoBoxPanel.setOpaque(false);
infoBoxPanel.setLayout(new BoxLayout(infoBoxPanel, BoxLayout.Y_AXIS));
layoutPanel.add(infoBoxPanel, BorderLayout.CENTER);
layoutPanel.add(overallPanel, BorderLayout.NORTH);
try
{
@@ -96,7 +126,6 @@ class XpPanel extends PluginPanel
{
break;
}
infoBoxes.put(skill, new XpInfoBox(xpTrackerPlugin, client, infoBoxPanel, skill, iconManager));
}
}
@@ -104,6 +133,10 @@ class XpPanel extends PluginPanel
{
log.warn(null, e);
}
errorPanel.setContent("Exp trackers", "You have not gained experience yet.");
errorPanel.setBorder(new EmptyBorder(0, 0, 0, 0));
add(errorPanel);
}
static String buildXpTrackerUrl(final Actor player, final Skill skill)
@@ -150,30 +183,23 @@ class XpPanel extends PluginPanel
}
}
void updateTotal(XpSnapshotTotal xpSnapshotTotal)
public void updateTotal(XpSnapshotTotal xpSnapshotTotal)
{
// if player has gained exp and hasn't switched displays yet, hide error panel and show overall info
if (xpSnapshotTotal.getXpGainedInSession() > 0 && !overallPanel.isVisible())
{
overallPanel.setVisible(true);
remove(errorPanel);
}
SwingUtilities.invokeLater(() -> rebuildAsync(xpSnapshotTotal));
}
private void rebuildAsync(XpSnapshotTotal xpSnapshotTotal)
{
totalXpGained.setText(formatLine(xpSnapshotTotal.getXpGainedInSession(), "total xp gained"));
totalXpHr.setText(formatLine(xpSnapshotTotal.getXpPerHour(), "total xp/hr"));
overallExpGained.setText(XpInfoBox.htmlLabel("Gained: ", xpSnapshotTotal.getXpGainedInSession()));
overallExpHour.setText(XpInfoBox.htmlLabel("Per hour: ", xpSnapshotTotal.getXpPerHour()));
}
static String formatLine(double number, String description)
{
String numberStr;
if (number < 100000)
{
numberStr = StackFormatter.formatNumber(number);
}
else
{
int num = (int) (Math.log(number) / Math.log(1000));
numberStr = String.format("%.1f%c", number / Math.pow(1000, num), "KMB".charAt(num - 1));
}
return numberStr + " " + description;
}
}

View File

@@ -122,6 +122,7 @@ public class XpTrackerPlugin extends Plugin
navButton = NavigationButton.builder()
.name("XP Tracker")
.icon(icon)
.priority(2)
.panel(xpPanel)
.build();

View File

@@ -67,11 +67,12 @@ import net.runelite.client.events.PluginToolbarButtonAdded;
import net.runelite.client.events.PluginToolbarButtonRemoved;
import net.runelite.client.events.TitleToolbarButtonAdded;
import net.runelite.client.events.TitleToolbarButtonRemoved;
import net.runelite.client.ui.skin.SubstanceRuneLiteLookAndFeel;
import net.runelite.client.input.KeyManager;
import net.runelite.client.util.OSType;
import net.runelite.client.util.OSXUtil;
import net.runelite.client.util.SwingUtil;
import org.pushingpixels.substance.api.skin.SubstanceGraphiteLookAndFeel;
import org.pushingpixels.substance.internal.SubstanceSynapse;
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceTitlePaneUtilities;
@@ -327,7 +328,7 @@ public class ClientUI
SwingUtil.setupDefaults();
// Use substance look and feel
SwingUtil.setTheme(new SubstanceGraphiteLookAndFeel());
SwingUtil.setTheme(new SubstanceRuneLiteLookAndFeel());
// Use custom UI font
SwingUtil.setFont(FontManager.getRunescapeFont());
@@ -362,6 +363,8 @@ public class ClientUI
navContainer.setLayout(new BorderLayout(0, 0));
navContainer.setMinimumSize(new Dimension(0, 0));
navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE));
// To reduce substance's colorization (tinting)
navContainer.putClientProperty(SubstanceSynapse.COLORIZATION_FACTOR, 1.0);
container.add(navContainer);
pluginToolbar = new ClientPluginToolbar();
@@ -565,6 +568,7 @@ public class ClientUI
/**
* Get offset of game canvas in game window
*
* @return game canvas offset
*/
public Point getCanvasOffset()

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018, Psikoi <https://github.com/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.ui;
import java.awt.Color;
/**
* This class serves to hold commonly used UI colors.
*/
public class ColorScheme
{
/* The orange color used for the branding's accents */
public static final Color BRAND_ORANGE = new Color(220, 138, 0);
/* The orange color used for the branding's accents, with lowered opacity */
public static final Color BRAND_ORANGE_TRANSPARENT = new Color(220, 138, 0, 120);
public static final Color DARKER_GRAY_COLOR = new Color(30, 30, 30);
public static final Color DARK_GRAY_COLOR = new Color(40, 40, 40);
public static final Color MEDIUM_GRAY_COLOR = new Color(77, 77, 77);
public static final Color LIGHT_GRAY_COLOR = new Color(165, 165, 165);
public static final Color DARK_GRAY_HOVER_COLOR = new Color(35, 35, 35);
/* The color for the green progress bar (used in ge offers, farming tracker, etc)*/
public static final Color PROGRESS_COMPLETE_COLOR = new Color(55, 240, 70);
/* The color for the red progress bar (used in ge offers, farming tracker, etc)*/
public static final Color PROGRESS_ERROR_COLOR = new Color(230, 30, 30);
/* The color for the orange progress bar (used in ge offers, farming tracker, etc)*/
public static final Color PROGRESS_INPROGRESS_COLOR = new Color(230, 150, 30);
/* The color for the price indicator in the ge search results */
public static final Color GRAND_EXCHANGE_PRICE = new Color(110, 225, 110);
/* The color for the high alch indicator in the ge search results */
public static final Color GRAND_EXCHANGE_ALCH = new Color(240, 207, 123);
/* The background color of the scrollbar's track */
public static final Color SCROLL_TRACK_COLOR = new Color(25, 25, 25);
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.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;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Insets;
import java.util.function.Function;
/**
* Grid layout implementation with support for cells with unequal size.
*
* See https://www.javaworld.com/article/2077486/core-java/java-tip-121--flex-your-grid-layout.html
*/
public class DynamicGridLayout extends GridLayout
{
public DynamicGridLayout()
{
this(1, 0, 0, 0);
}
public DynamicGridLayout(int rows, int cols)
{
this(rows, cols, 0, 0);
}
public DynamicGridLayout(int rows, int cols, int hgap, int vgap)
{
super(rows, cols, hgap, vgap);
}
@Override
public Dimension preferredLayoutSize(Container parent)
{
synchronized (parent.getTreeLock())
{
return calculateSize(parent, Component::getPreferredSize);
}
}
@Override
public Dimension minimumLayoutSize(Container parent)
{
synchronized (parent.getTreeLock())
{
return calculateSize(parent, Component::getMinimumSize);
}
}
@Override
public void layoutContainer(Container parent)
{
synchronized (parent.getTreeLock())
{
final Insets insets = parent.getInsets();
final int ncomponents = parent.getComponentCount();
int nrows = getRows();
int ncols = getColumns();
if (ncomponents == 0)
{
return;
}
if (nrows > 0)
{
ncols = (ncomponents + nrows - 1) / nrows;
}
else
{
nrows = (ncomponents + ncols - 1) / ncols;
}
final int hgap = getHgap();
final int vgap = getVgap();
// scaling factors
final Dimension pd = preferredLayoutSize(parent);
final double sw = (1.0 * parent.getWidth()) / pd.width;
final double sh = (1.0 * parent.getHeight()) / pd.height;
final int[] w = new int[ncols];
final int[] h = new int[nrows];
// calculate dimensions for all components + apply scaling
for (int i = 0; i < ncomponents; i++)
{
final int r = i / ncols;
final int c = i % ncols;
final Component comp = parent.getComponent(i);
final Dimension d = comp.getPreferredSize();
d.width = (int) (sw * d.width);
d.height = (int) (sh * d.height);
if (w[c] < d.width)
{
w[c] = d.width;
}
if (h[r] < d.height)
{
h[r] = d.height;
}
}
// Apply new bounds to all child components
for (int c = 0, x = insets.left; c < ncols; c++)
{
for (int r = 0, y = insets.top; r < nrows; r++)
{
int i = r * ncols + c;
if (i < ncomponents)
{
parent.getComponent(i).setBounds(x, y, w[c], h[r]);
}
y += h[r] + vgap;
}
x += w[c] + hgap;
}
}
}
/**
* Calculate outer size of the layout based on it's children and sizer
* @param parent parent component
* @param sizer functioning returning dimension of the child component
* @return outer size
*/
private Dimension calculateSize(final Container parent, final Function<Component, Dimension> sizer)
{
final int ncomponents = parent.getComponentCount();
int nrows = getRows();
int ncols = getColumns();
if (nrows > 0)
{
ncols = (ncomponents + nrows - 1) / nrows;
}
else
{
nrows = (ncomponents + ncols - 1) / ncols;
}
final int[] w = new int[ncols];
final int[] h = new int[nrows];
// Calculate dimensions for all components
for (int i = 0; i < ncomponents; i++)
{
final int r = i / ncols;
final int c = i % ncols;
final Component comp = parent.getComponent(i);
final Dimension d = sizer.apply(comp);
if (w[c] < d.width)
{
w[c] = d.width;
}
if (h[r] < d.height)
{
h[r] = d.height;
}
}
// Calculate total width and height of the layout
int nw = 0;
for (int j = 0; j < ncols; j++)
{
nw += w[j];
}
int nh = 0;
for (int i = 0; i < nrows; i++)
{
nh += h[i];
}
final Insets insets = parent.getInsets();
// Apply insets and horizontal and vertical gap to layout
return new Dimension(
insets.left + insets.right + nw + (ncols - 1) * getHgap(),
insets.top + insets.bottom + nh + (nrows - 1) * getVgap());
}
}

View File

@@ -74,6 +74,12 @@ public class NavigationButton
*/
private PluginPanel panel;
/**
* The order in which the button should be displayed in the side bar. (from lower to higher)
*/
private int priority;
/**
* Map of key-value pairs for setting the popup menu
*/

View File

@@ -60,13 +60,14 @@ public abstract class PluginPanel extends JPanel
{
setBorder(BORDER_PADDING);
setLayout(new GridLayout(0, 1, 0, 3));
setBackground(ColorScheme.DARK_GRAY_COLOR);
final JPanel northPanel = new JPanel();
northPanel.setLayout(new BorderLayout());
northPanel.add(this, BorderLayout.NORTH);
northPanel.setBackground(ColorScheme.DARK_GRAY_COLOR);
scrollPane = new JScrollPane(northPanel);
scrollPane.getVerticalScrollBar().setUnitIncrement(16); //Otherwise scrollspeed is really slow
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
wrappedPanel = new JPanel();

View File

@@ -24,8 +24,8 @@
*/
package net.runelite.client.ui;
import com.google.common.collect.ComparisonChain;
import com.google.common.eventbus.EventBus;
import java.util.Comparator;
import java.util.TreeSet;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -39,7 +39,11 @@ import net.runelite.client.events.PluginToolbarButtonRemoved;
public class PluginToolbar
{
private final EventBus eventBus;
private final TreeSet<NavigationButton> buttons = new TreeSet<>(Comparator.comparing(NavigationButton::getName));
private final TreeSet<NavigationButton> buttons = new TreeSet<>((a, b) ->
ComparisonChain.start()
.compare(a.getPriority(), b.getPriority())
.compare(a.getName(), b.getName())
.result());
@Inject
private PluginToolbar(final EventBus eventBus)

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2017, Psikoi <https://github.com/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.ui.components;
import java.awt.Color;
import java.awt.Component;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import javax.swing.border.EmptyBorder;
import net.runelite.client.ui.ColorScheme;
/**
* A custom list renderer to avoid substance's weird coloring.
* Substance was making selected items' foreground color black, this
* was very hard to see in the dark gray background, this makes the selected
* item white and adds some padding to the elements for more readable list.
*/
public final class ComboBoxListRenderer extends JLabel implements ListCellRenderer
{
@Override
public Component getListCellRendererComponent(JList list, Object o, int index, boolean isSelected, boolean cellHasFocus)
{
if (isSelected)
{
setBackground(ColorScheme.DARK_GRAY_COLOR);
setForeground(Color.WHITE);
}
else
{
setBackground(list.getBackground());
setForeground(ColorScheme.LIGHT_GRAY_COLOR);
}
setBorder(new EmptyBorder(5, 5, 5, 0));
String text = (String) o.toString();
setText(text);
return this;
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 2018, Psikoi <https://github.com/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.ui.components;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JScrollBar;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollBarUI;
import lombok.Setter;
import net.runelite.client.ui.ColorScheme;
/**
* This scroll bar UI is to be used for the "RuneLite Obsidian" client theme.
* It is a part of the client's redesign as detailed on issue #1342
*/
public class CustomScrollBarUI extends BasicScrollBarUI
{
/* The background color of the bar's thumb */
@Setter
private Color thumbColor = ColorScheme.MEDIUM_GRAY_COLOR;
/* The background color of the bar's track */
@Setter
private Color trackColor = ColorScheme.SCROLL_TRACK_COLOR;
/**
* Overrides the painting of the bar's track (the darker part underneath that extends
* the full page length).
*/
@Override
protected void paintTrack(Graphics graphics, JComponent jComponent, Rectangle rectangle)
{
graphics.setColor(trackColor);
graphics.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
}
/**
* Overrides the painting of the bar's thumb (the lighter part on top that users
* use to slide up and down the page).
*/
@Override
protected void paintThumb(Graphics graphics, JComponent jComponent, Rectangle rectangle)
{
graphics.setColor(thumbColor);
graphics.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
}
/**
* Creates an empty JButton to be used as the scroll bar's arrows (to disable the arrows).
*/
protected JButton createEmptyButton()
{
JButton button = new JButton();
Dimension zeroDim = new Dimension(0, 0);
button.setPreferredSize(zeroDim);
button.setMinimumSize(zeroDim);
button.setMaximumSize(zeroDim);
return button;
}
public static ComponentUI createUI(JComponent c)
{
JScrollBar bar = (JScrollBar) c;
bar.setUnitIncrement(16);
bar.setPreferredSize(new Dimension(7, 0));
return new CustomScrollBarUI();
}
/**
* Applies an empty button to the decrease (down arrow) button.
*/
@Override
protected JButton createDecreaseButton(int orientation)
{
return createEmptyButton();
}
/**
* Applies an empty button to the increase (up arrow) button.
*/
@Override
protected JButton createIncreaseButton(int orientation)
{
return createEmptyButton();
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* Copyright (c) 2018, Psikoi <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:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 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 HOLDER 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.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.text.Document;
import net.runelite.client.ui.ColorScheme;
/**
* This component is a JTextField with an icon on its left side.
*/
public class IconTextField extends JPanel
{
private final JTextField textField;
//to support gifs, the icon needs to be wrapped in a JLabel
private final JLabel iconWrapperLabel;
//the default background color, this needs to be stored for hover effects
private Color backgroundColor = ColorScheme.DARKER_GRAY_COLOR;
//the default hover background color, this needs to be stored for hover effects
private Color hoverBackgroundColor;
// the input can be blocked (no clicking, no editing, no hover effects)
private boolean blocked;
public IconTextField()
{
setLayout(new BorderLayout());
this.iconWrapperLabel = new JLabel();
this.iconWrapperLabel.setPreferredSize(new Dimension(30, 0));
this.iconWrapperLabel.setVerticalAlignment(JLabel.CENTER);
this.iconWrapperLabel.setHorizontalAlignment(JLabel.CENTER);
this.textField = new JTextField();
this.textField.setBorder(null);
this.textField.setOpaque(false);
this.textField.setSelectedTextColor(Color.WHITE);
this.textField.setSelectionColor(ColorScheme.BRAND_ORANGE_TRANSPARENT);
add(iconWrapperLabel, BorderLayout.WEST);
add(textField, BorderLayout.CENTER);
textField.addMouseListener(new MouseAdapter()
{
@Override
public void mouseEntered(MouseEvent mouseEvent)
{
if (blocked)
{
return;
}
if (hoverBackgroundColor != null)
{
IconTextField.super.setBackground(hoverBackgroundColor);
}
}
@Override
public void mouseExited(MouseEvent mouseEvent)
{
IconTextField.super.setBackground(backgroundColor);
}
});
}
public void addActionListener(ActionListener actionListener)
{
textField.addActionListener(actionListener);
}
public void setIcon(ImageIcon icon)
{
iconWrapperLabel.setIcon(icon);
}
public String getText()
{
return textField.getText();
}
public void setText(String text)
{
textField.setText(text);
}
@Override
public void setBackground(Color color)
{
if (color == null)
{
return;
}
super.setBackground(color);
this.backgroundColor = color;
}
public void addInputKeyListener(KeyListener l)
{
textField.addKeyListener(l);
}
public void removeInputKeyListener(KeyListener l)
{
textField.removeKeyListener(l);
}
public void setHoverBackgroundColor(Color hoverBackgroundColor)
{
if (hoverBackgroundColor == null)
{
return;
}
this.hoverBackgroundColor = hoverBackgroundColor;
}
public void setEditable(boolean editable)
{
this.blocked = !editable;
textField.setEditable(editable);
textField.setFocusable(editable);
if (!editable)
{
super.setBackground(backgroundColor);
}
}
public Document getDocument()
{
return textField.getDocument();
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2018, Psikoi <https://github.com/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:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 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 HOLDER 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.BorderLayout;
import java.awt.Color;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
/**
* A component to display an error/info message (to be used on a plugin panel)
* Example uses are: no ge search results found, no ge offers found.
*/
public class PluginErrorPanel extends JPanel
{
private final JLabel noResultsTitle = new JShadowedLabel();
private final JLabel noResultsDescription = new JShadowedLabel();
public PluginErrorPanel()
{
setOpaque(false);
setBorder(new EmptyBorder(50, 10, 0, 10));
setLayout(new BorderLayout());
noResultsTitle.setForeground(Color.WHITE);
noResultsTitle.setHorizontalAlignment(SwingConstants.CENTER);
noResultsDescription.setFont(FontManager.getRunescapeSmallFont());
noResultsDescription.setForeground(Color.GRAY);
noResultsDescription.setHorizontalAlignment(SwingConstants.CENTER);
add(noResultsTitle, BorderLayout.NORTH);
add(noResultsDescription, BorderLayout.CENTER);
setVisible(false);
}
/**
* Changes the content of the panel to the given parameters.
* The description has to be wrapped in html so that its text can be wrapped.
*/
public void setContent(String title, String description)
{
noResultsTitle.setText(title);
noResultsDescription.setText("<html><body style = 'text-align:center'>" + description + "</body></html>");
setVisible(true);
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2018, Psikoi <https://github.com/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.ui.components;
import java.awt.BorderLayout;
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;
import net.runelite.client.ui.FontManager;
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
{
@Setter
private int maximumValue;
@Setter
private int value;
private final JLabel leftLabel = new JShadowedLabel();
private final JLabel rightLabel = new JShadowedLabel();
private final JLabel centerLabel = new JShadowedLabel();
public ProgressBar()
{
setLayout(new BorderLayout());
setBackground(Color.GREEN.darker());
setPreferredSize(new Dimension(100, 16));
leftLabel.setFont(FontManager.getRunescapeSmallFont());
leftLabel.setForeground(Color.WHITE);
leftLabel.setBorder(new EmptyBorder(2, 5, 0, 0));
rightLabel.setFont(FontManager.getRunescapeSmallFont());
rightLabel.setForeground(Color.WHITE);
rightLabel.setBorder(new EmptyBorder(2, 0, 0, 5));
centerLabel.setFont(FontManager.getRunescapeSmallFont());
centerLabel.setForeground(Color.WHITE);
centerLabel.setHorizontalAlignment(SwingConstants.CENTER);
centerLabel.setBorder(new EmptyBorder(2, 0, 0, 0));
add(leftLabel, BorderLayout.WEST);
add(centerLabel, BorderLayout.CENTER);
add(rightLabel, BorderLayout.EAST);
}
@Override
public void paint(Graphics g)
{
double percentage = getPercentage();
int topWidth = (int) (getSize().width * (percentage / 100));
super.paint(g);
g.setColor(getForeground());
g.fillRect(0, 0, topWidth, 16);
super.paintComponents(g);
}
public void setLeftLabel(String txt)
{
this.leftLabel.setText(txt);
}
public void setRightLabel(String txt)
{
this.rightLabel.setText(txt);
}
public void setCenterLabel(String txt)
{
this.centerLabel.setText(txt);
}
public double getPercentage()
{
if (value == 0)
{
return 0;
}
return (value * 100) / maximumValue;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -23,54 +23,71 @@
* 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.hiscore;
package net.runelite.client.ui.components;
import java.awt.Graphics;
import java.awt.Insets;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JTextField;
import javax.swing.border.Border;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JPanel;
import lombok.Setter;
import net.runelite.client.ui.ColorScheme;
public class IconTextField extends JTextField
/**
* A progress bar to be displayed underneath the GE offer item panels
*/
public class ThinProgressBar extends JPanel
{
private Border border;
private Icon icon;
@Setter
private int maximumValue;
@Override
public void setBorder(Border border)
@Setter
private int value;
private final JPanel topBar = new JPanel();
public ThinProgressBar()
{
this.border = border;
setLayout(new BorderLayout());
setBackground(Color.GREEN.darker());
if (icon == null)
topBar.setPreferredSize(new Dimension(100, 4));
topBar.setBackground(ColorScheme.PROGRESS_COMPLETE_COLOR);
add(topBar, BorderLayout.WEST);
}
/**
* Updates the UI based on the percentage progress
*/
public void update()
{
double percentage = getPercentage();
int topWidth = (int) (getSize().width * (percentage / 100));
topBar.setPreferredSize(new Dimension(topWidth, 4));
topBar.repaint();
revalidate();
repaint();
}
public double getPercentage()
{
if (value == 0)
{
super.setBorder(border);
}
else
{
Border margin = BorderFactory.createEmptyBorder(0, icon.getIconWidth() + 4, 0, 0);
Border compound = BorderFactory.createCompoundBorder(border, margin);
super.setBorder(compound);
return 0;
}
return (value * 100) / maximumValue;
}
@Override
public void paintComponent(Graphics graphics)
public void setForeground(Color color)
{
super.paintComponent(graphics);
Insets iconInsets = border.getBorderInsets(this);
icon.paintIcon(this, graphics, iconInsets.left, iconInsets.top);
if (topBar != null)
{
topBar.setBackground(color);
}
setBackground(color.darker());
}
public void setIcon(Icon icon)
{
this.icon = icon;
resetBorder();
}
private void resetBorder()
{
setBorder(border);
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2018, Psikoi <https://github.com/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.ui.components.materialtabs;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import lombok.Getter;
import lombok.Setter;
import net.runelite.client.ui.ColorScheme;
/**
* This class represents a Material Design inspired tab.
* <p>
* Each tab will communicate with it's containing group when it's clicked
* and that group will display the tab's content on it's own display.
*
* @author Psikoi
*/
public class MaterialTab extends JLabel
{
private static final Border SELECTED_BORDER = new CompoundBorder(
BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.BRAND_ORANGE),
BorderFactory.createEmptyBorder(5, 10, 5, 10));
private static final Border UNSELECTED_BORDER = BorderFactory
.createEmptyBorder(5, 10, 5, 10);
/* The tab's containing group */
private final MaterialTabGroup group;
/* The tab's associated content display */
@Getter
private final JComponent content;
/* To be execuded when the tab is selected */
@Setter
private Runnable onSelectEvent;
@Getter
private boolean selected;
public MaterialTab(String string, MaterialTabGroup group, JComponent content)
{
super(string);
this.group = group;
this.content = content;
if (selected)
{
select();
}
else
{
unselect();
}
addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent mouseEvent)
{
group.select(MaterialTab.this);
}
});
}
public void select()
{
setBorder(SELECTED_BORDER);
setForeground(Color.WHITE);
selected = true;
if (onSelectEvent != null)
{
onSelectEvent.run();
}
}
public void unselect()
{
setBorder(UNSELECTED_BORDER);
setForeground(Color.GRAY);
selected = false;
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (c) 2018, Psikoi <https://github.com/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.ui.components.materialtabs;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
/**
* This class will be a container (group) for the new Material Tabs. It will
* contain a list of tabs and a display (JPanel). When a tab is selected, the
* JPanel "display" will display the content associated with that tab.
* <p>
* How to use these tabs:
* <ol>
* <li>1 - Create displays (JPanels) for each tab</li>
* <li>2 - Create an empty JPanel to serve as the group's display</li>
* <li>3 - Create a new MaterialGroup, passing the panel in step 2 as a param</li>
* <li>4 - Create new tabs, passing the group in step 3 and one of the panels in
* step 1 as params</li>
* <li>5 - Add the tabs to the group using the MaterialTabGroup#addTab method</li>
* <li>6 - Select one of the tabs using the MaterialTab#select method</li>
* </ol>
*
* @author Psikoi
*/
public class MaterialTabGroup extends JPanel
{
/* The panel on which the content tab's content will be displayed on. */
private final JPanel display;
/* A list of all the tabs contained in this group. */
private final List<MaterialTab> tabs = new ArrayList<>();
public MaterialTabGroup(JPanel display)
{
this.display = display;
this.display.setLayout(new BorderLayout());
setLayout(new FlowLayout(FlowLayout.CENTER, 8, 0));
setOpaque(false);
}
/* Returns the tab on a certain index. */
public MaterialTab getTab(int index)
{
if (tabs == null || tabs.isEmpty())
{
return null;
}
return tabs.get(index);
}
public void addTab(MaterialTab tab)
{
tabs.add(tab);
add(tab, BorderLayout.NORTH);
}
/***
* Selects a tab from the group, and sets the display's content to the
* tab's associated content.
* @param selectedTab - The tab to select
*/
public boolean select(MaterialTab selectedTab)
{
if (!tabs.contains(selectedTab))
{
return false;
}
display.removeAll();
for (MaterialTab tab : tabs)
{
if (tab.equals(selectedTab))
{
tab.select();
display.add(tab.getContent());
display.revalidate();
display.repaint();
}
else
{
tab.unselect();
}
}
return true;
}
}

View File

@@ -22,7 +22,7 @@
* (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;
package net.runelite.client.ui.components.shadowlabel;
import java.awt.Color;
import java.awt.Point;

View File

@@ -22,7 +22,7 @@
* (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;
package net.runelite.client.ui.components.shadowlabel;
import java.awt.Graphics;
import javax.swing.JLabel;

View File

@@ -0,0 +1,220 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2018, Psikoi <https://github.com/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.ui.skin;
import javax.swing.AbstractButton;
import org.pushingpixels.substance.api.ComponentState;
import org.pushingpixels.substance.api.SubstanceColorSchemeBundle;
import org.pushingpixels.substance.api.SubstanceSkin;
import org.pushingpixels.substance.api.SubstanceSlices.ColorSchemeAssociationKind;
import org.pushingpixels.substance.api.SubstanceSlices.DecorationAreaType;
import org.pushingpixels.substance.api.colorscheme.ColorSchemeSingleColorQuery;
import org.pushingpixels.substance.api.colorscheme.SubstanceColorScheme;
import org.pushingpixels.substance.api.painter.border.ClassicBorderPainter;
import org.pushingpixels.substance.api.painter.border.CompositeBorderPainter;
import org.pushingpixels.substance.api.painter.border.DelegateBorderPainter;
import org.pushingpixels.substance.api.painter.decoration.MatteDecorationPainter;
import org.pushingpixels.substance.api.painter.fill.FractionBasedFillPainter;
import org.pushingpixels.substance.api.painter.highlight.ClassicHighlightPainter;
import org.pushingpixels.substance.api.painter.overlay.BottomLineOverlayPainter;
import org.pushingpixels.substance.api.painter.overlay.BottomShadowOverlayPainter;
import org.pushingpixels.substance.api.painter.overlay.TopBezelOverlayPainter;
import org.pushingpixels.substance.api.painter.overlay.TopLineOverlayPainter;
import org.pushingpixels.substance.api.shaper.ClassicButtonShaper;
import org.pushingpixels.substance.internal.utils.SubstanceColorUtilities;
public class ObsidianSkin extends SubstanceSkin
{
/**
* Display name for <code>this</code> skin.
*/
private static final String NAME = "RuneLite";
/**
* Creates a new <code>RuneLite</code> skin.
*/
ObsidianSkin()
{
final SubstanceSkin.ColorSchemes schemes = SubstanceSkin
.getColorSchemes(getClass().getResource(NAME + ".colorschemes"));
final SubstanceColorScheme activeScheme = schemes.get("RuneLite Active");
final SubstanceColorScheme enabledScheme = schemes.get("RuneLite Enabled");
final SubstanceColorSchemeBundle defaultSchemeBundle = new SubstanceColorSchemeBundle(
activeScheme, enabledScheme, enabledScheme);
defaultSchemeBundle.registerColorScheme(enabledScheme, 0.6f,
ComponentState.DISABLED_UNSELECTED);
defaultSchemeBundle.registerColorScheme(activeScheme, 0.6f,
ComponentState.DISABLED_SELECTED);
// borders
final SubstanceColorScheme borderDisabledSelectedScheme = schemes
.get("RuneLite Selected Disabled Border");
final SubstanceColorScheme borderScheme = schemes.get("RuneLite Border");
defaultSchemeBundle.registerColorScheme(borderDisabledSelectedScheme,
ColorSchemeAssociationKind.BORDER, ComponentState.DISABLED_SELECTED);
defaultSchemeBundle.registerColorScheme(borderScheme, ColorSchemeAssociationKind.BORDER);
// marks
final SubstanceColorScheme markActiveScheme = schemes.get("RuneLite Mark Active");
defaultSchemeBundle.registerColorScheme(markActiveScheme, ColorSchemeAssociationKind.MARK,
ComponentState.getActiveStates());
defaultSchemeBundle.registerColorScheme(markActiveScheme, 0.6f,
ColorSchemeAssociationKind.MARK, ComponentState.DISABLED_SELECTED,
ComponentState.DISABLED_UNSELECTED);
// separators
final SubstanceColorScheme separatorScheme = schemes.get("RuneLite Separator");
defaultSchemeBundle.registerColorScheme(separatorScheme,
ColorSchemeAssociationKind.SEPARATOR);
// tab borders
defaultSchemeBundle.registerColorScheme(schemes.get("RuneLite Tab Border"),
ColorSchemeAssociationKind.TAB_BORDER, ComponentState.getActiveStates());
final SubstanceColorScheme watermarkScheme = schemes.get("RuneLite Watermark");
this.registerDecorationAreaSchemeBundle(defaultSchemeBundle, watermarkScheme,
DecorationAreaType.NONE);
final SubstanceColorSchemeBundle decorationsSchemeBundle = new SubstanceColorSchemeBundle(
activeScheme, enabledScheme, enabledScheme);
decorationsSchemeBundle.registerColorScheme(enabledScheme, 0.5f,
ComponentState.DISABLED_UNSELECTED);
// borders
decorationsSchemeBundle.registerColorScheme(borderDisabledSelectedScheme,
ColorSchemeAssociationKind.BORDER, ComponentState.DISABLED_SELECTED);
decorationsSchemeBundle.registerColorScheme(borderScheme,
ColorSchemeAssociationKind.BORDER);
// marks
decorationsSchemeBundle.registerColorScheme(markActiveScheme,
ColorSchemeAssociationKind.MARK, ComponentState.getActiveStates());
// separators
final SubstanceColorScheme separatorDecorationsScheme = schemes
.get("RuneLite Decorations Separator");
decorationsSchemeBundle.registerColorScheme(separatorDecorationsScheme,
ColorSchemeAssociationKind.SEPARATOR);
final SubstanceColorScheme decorationsWatermarkScheme = schemes
.get("RuneLite Decorations Watermark");
this.registerDecorationAreaSchemeBundle(decorationsSchemeBundle, decorationsWatermarkScheme,
DecorationAreaType.TOOLBAR, DecorationAreaType.GENERAL, DecorationAreaType.FOOTER);
final SubstanceColorSchemeBundle headerSchemeBundle = new SubstanceColorSchemeBundle(activeScheme,
enabledScheme, enabledScheme);
headerSchemeBundle.registerColorScheme(enabledScheme, 0.5f,
ComponentState.DISABLED_UNSELECTED);
// borders
final SubstanceColorScheme headerBorderScheme = schemes.get("RuneLite Header Border");
headerSchemeBundle.registerColorScheme(borderDisabledSelectedScheme,
ColorSchemeAssociationKind.BORDER, ComponentState.DISABLED_SELECTED);
headerSchemeBundle.registerColorScheme(headerBorderScheme,
ColorSchemeAssociationKind.BORDER);
// marks
headerSchemeBundle.registerColorScheme(markActiveScheme, ColorSchemeAssociationKind.MARK,
ComponentState.getActiveStates());
headerSchemeBundle.registerHighlightColorScheme(activeScheme, 0.7f,
ComponentState.ROLLOVER_UNSELECTED, ComponentState.ROLLOVER_ARMED,
ComponentState.ARMED);
headerSchemeBundle.registerHighlightColorScheme(activeScheme, 0.8f,
ComponentState.SELECTED);
headerSchemeBundle.registerHighlightColorScheme(activeScheme, 1.0f,
ComponentState.ROLLOVER_SELECTED);
final SubstanceColorScheme headerWatermarkScheme = schemes.get("RuneLite Header Watermark");
this.registerDecorationAreaSchemeBundle(headerSchemeBundle, headerWatermarkScheme,
DecorationAreaType.PRIMARY_TITLE_PANE, DecorationAreaType.SECONDARY_TITLE_PANE,
DecorationAreaType.HEADER);
setTabFadeStart(0.2);
setTabFadeEnd(0.9);
// Add overlay painters to paint drop shadows along the bottom
// edges of toolbars and footers
this.addOverlayPainter(BottomShadowOverlayPainter.getInstance(),
DecorationAreaType.TOOLBAR);
this.addOverlayPainter(BottomShadowOverlayPainter.getInstance(), DecorationAreaType.FOOTER);
// add an overlay painter to paint a dark line along the bottom
// edge of toolbars
final BottomLineOverlayPainter toolbarBottomLineOverlayPainter = new BottomLineOverlayPainter(
(SubstanceColorScheme scheme) -> scheme.getUltraDarkColor().darker());
this.addOverlayPainter(toolbarBottomLineOverlayPainter, DecorationAreaType.TOOLBAR);
// add an overlay painter to paint a dark line along the bottom
// edge of toolbars
final TopLineOverlayPainter toolbarTopLineOverlayPainter = new TopLineOverlayPainter(
(SubstanceColorScheme scheme) -> SubstanceColorUtilities
.getAlphaColor(scheme.getForegroundColor(), 32));
this.addOverlayPainter(toolbarTopLineOverlayPainter, DecorationAreaType.TOOLBAR);
// add an overlay painter to paint a bezel line along the top
// edge of footer
final TopBezelOverlayPainter footerTopBezelOverlayPainter = new TopBezelOverlayPainter(
(SubstanceColorScheme scheme) -> scheme.getUltraDarkColor().darker(),
(SubstanceColorScheme scheme) -> SubstanceColorUtilities
.getAlphaColor(scheme.getForegroundColor(), 32));
this.addOverlayPainter(footerTopBezelOverlayPainter, DecorationAreaType.FOOTER);
this.setTabFadeStart(0.18);
this.setTabFadeEnd(0.18);
// Set button shaper to use "flat" design
this.buttonShaper = new ClassicButtonShaper()
{
@Override
public float getCornerRadius(AbstractButton button, float insets)
{
return 0;
}
};
this.watermark = null;
this.fillPainter = new FractionBasedFillPainter("RuneLite",
new float[]{0.0f, 0.5f, 1.0f},
new ColorSchemeSingleColorQuery[]{ColorSchemeSingleColorQuery.ULTRALIGHT,
ColorSchemeSingleColorQuery.LIGHT, ColorSchemeSingleColorQuery.LIGHT});
this.decorationPainter = new MatteDecorationPainter();
this.highlightPainter = new ClassicHighlightPainter();
this.borderPainter = new CompositeBorderPainter("RuneLite", new ClassicBorderPainter(),
new DelegateBorderPainter("RuneLite Inner", new ClassicBorderPainter(), 0x40FFFFFF,
0x20FFFFFF, 0x00FFFFFF,
(SubstanceColorScheme scheme) -> scheme.tint(0.2f)));
}
@Override
public String getDisplayName()
{
return NAME;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2018, Psikoi <https://github.com/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.ui.skin;
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
public class SubstanceRuneLiteLookAndFeel extends SubstanceLookAndFeel
{
public SubstanceRuneLiteLookAndFeel()
{
super(new ObsidianSkin());
}
}

View File

@@ -128,6 +128,61 @@ public class StackFormatter
}
}
/**
* Convert a quantity to stack size as it would
* appear in RuneScape. (with decimals)
* <p>
* This differs from quantityToRSStack in that it displays
* decimals. Ex: 27100 is 27,1k (not 27k)
* <p>
* This uses the NumberFormat singleton instead of the
* NUMBER_FORMATTER variable to ensure the UK locale.
*
* @param quantity The quantity to convert.
* @return The stack size as it would appear in RS, with decimals,
* with K after 100,000 and M after 10,000,000
*/
public static String quantityToRSDecimalStack(int quantity)
{
if (quantity < 10_000)
{
return Integer.toString(quantity);
}
else if (quantity < 1_000_000)
{
if (quantity % 1000 == 0)
{
return quantity / 1000 + "K";
}
return NUMBER_FORMATTER.format(quantity).substring(0, Integer.toString(quantity).length() - 1) + "K";
}
else if (quantity < 10_000_000)
{
if (quantity % 1_000_000 == 0)
{
return quantity / 1_000_000 + "M";
}
return NUMBER_FORMATTER.format(quantity).substring(0, Integer.toString(quantity).length() - 4) + "M";
}
else if (quantity < 1_000_000_000)
{
if (quantity % 1_000_000 == 0)
{
return quantity / 1_000_000 + "M";
}
return NUMBER_FORMATTER.format(quantity).substring(0, Integer.toString(quantity).length() - 4) + "M";
}
else
{
if (quantity % 1_000_000_000 == 0)
{
return quantity / 1_000_000_000 + "B";
}
return NUMBER_FORMATTER.format(quantity).substring(0, Integer.toString(quantity).length() - 7) + "B";
}
}
/**
* Converts a string representation of a stack
* back to (close to) it's original value.
@@ -147,8 +202,8 @@ public class StackFormatter
*
* @param number the long number to format
* @return the formatted String
* @exception ArithmeticException if rounding is needed with rounding
* mode being set to RoundingMode.UNNECESSARY
* @throws ArithmeticException if rounding is needed with rounding
* mode being set to RoundingMode.UNNECESSARY
* @see java.text.Format#format
*/
public static String formatNumber(final long number)
@@ -161,8 +216,8 @@ public class StackFormatter
*
* @param number the double number to format
* @return the formatted String
* @exception ArithmeticException if rounding is needed with rounding
* mode being set to RoundingMode.UNNECESSARY
* @throws ArithmeticException if rounding is needed with rounding
* mode being set to RoundingMode.UNNECESSARY
* @see java.text.Format#format
*/
public static String formatNumber(double number)

View File

@@ -25,12 +25,14 @@
package net.runelite.client.util;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
@@ -40,6 +42,8 @@ import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.LookupOp;
import java.awt.image.LookupTable;
import java.util.Enumeration;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
@@ -59,6 +63,7 @@ import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE;
import javax.swing.plaf.FontUIResource;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.components.CustomScrollBarUI;
import org.pushingpixels.substance.internal.SubstanceSynapse;
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
@@ -77,8 +82,13 @@ public class SwingUtil
// Force heavy-weight popups/tooltips.
// Prevents them from being obscured by the game applet.
ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false);
ToolTipManager.sharedInstance().setInitialDelay(300);
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
UIManager.put("Button.foreground", Color.WHITE);
UIManager.put("MenuItem.foreground", Color.WHITE);
UIManager.put("ScrollBarUI", CustomScrollBarUI.class.getName());
// Do not render shadows under popups/tooltips.
// Fixes black boxes under popups that are above the game applet.
System.setProperty("jgoodies.popupDropShadowEnabled", "false");
@@ -88,6 +98,48 @@ public class SwingUtil
System.setProperty("sun.awt.noerasebackground", "true");
}
/**
* Offsets an image in the grayscale (darkens/brightens) by an offset
*/
public static BufferedImage grayscaleOffset(BufferedImage image, int offset)
{
int numComponents = image.getColorModel().getNumComponents();
int index = numComponents - 1;
LookupTable lookup = new LookupTable(0, numComponents)
{
@Override
public int[] lookupPixel(int[] src, int[] dest)
{
if (dest[index] != 0)
{
dest[index] = dest[index] + offset;
if (dest[index] < 0)
{
dest[index] = 0;
}
else if (dest[index] > 255)
{
dest[index] = 255;
}
}
return dest;
}
};
LookupOp op = new LookupOp(lookup, new RenderingHints(null));
return op.filter(image, null);
}
/**
* Converts a given color to it's hexidecimal equivalent.
*/
public static String toHexColor(Color color)
{
return "#" + Integer.toHexString(color.getRGB()).substring(2);
}
/**
* Safely sets Swing theme
*
@@ -210,7 +262,7 @@ public class SwingUtil
result = JOptionPane.showConfirmDialog(
frame,
"Are you sure you want to exit?", "Exit",
JOptionPane .OK_CANCEL_OPTION,
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE);
}
}
@@ -258,6 +310,7 @@ public class SwingUtil
: navigationButton.getIcon();
final JButton button = new JButton();
button.setMaximumSize(new Dimension(30, 30));
button.setName(navigationButton.getName());
button.setToolTipText(navigationButton.getTooltip());
button.setIcon(new ImageIcon(scaledImage));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 566 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 B

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 B

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 692 B

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -2,4 +2,6 @@ runelite.title=RuneLite
runelite.version=${project.version}
runescape.version=${rs.version}
runelite.discord.appid=409416265891971072
runelite.discord.invite=https://discord.gg/R4BQ8tU
runelite.discord.invite=https://discord.gg/R4BQ8tU
runelite.github.link=https://github.com/runelite
runelite.patreon.link=https://www.patreon.com/runelite

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,166 @@
# Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
# Copyright (c) 2018, Psikoi <https://github.com/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.
RuneLite Enabled {
kind=Dark
colorUltraLight=#232323
colorExtraLight=#232323
colorLight=#232323
colorMid=#232323
colorDark=#232323
colorUltraDark=#232323
colorForeground=#C6C6C6
}
RuneLite Active {
kind=Light
colorUltraLight=#4e4e4e
colorExtraLight=#4e4e4e
colorLight=#4e4e4e
colorMid=#232323
colorDark=#232323
colorUltraDark=#232323
colorForeground=#000000
}
RuneLite Selected Disabled Border {
kind=Dark
colorUltraLight=#191919
colorExtraLight=#191919
colorLight=#191919
colorMid=#191919
colorDark=#191919
colorUltraDark=#191919
colorForeground=#C6C6C6
}
RuneLite Border {
kind=Dark
colorUltraLight=#191919
colorExtraLight=#191919
colorLight=#191919
colorMid=#191919
colorDark=#191919
colorUltraDark=#191919
colorForeground=#C6C6C6
}
RuneLite Tab Border {
kind=Light
colorUltraLight=#232323
colorExtraLight=#232323
colorLight=#232323
colorMid=#232323
colorDark=#232323
colorUltraDark=#232323
colorForeground=#232323
}
RuneLite Mark Active {
kind=Dark
colorUltraLight=#191919
colorExtraLight=#191919
colorLight=#191919
colorMid=#191919
colorDark=#191919
colorUltraDark=#191919
colorForeground=#191919
}
RuneLite Highlight {
kind=Light
colorUltraLight=#C6C6C6
colorExtraLight=#C6C6C6
colorLight=#C6C6C6
colorMid=#C6C6C6
colorDark=#C6C6C6
colorUltraDark=#C6C6C6
colorForeground=#191919
}
RuneLite Watermark {
kind=Light
colorUltraLight=#313131
colorExtraLight=#313131
colorLight=#313131
colorMid=#313131
colorDark=#313131
colorUltraDark=#313131
colorForeground=#C6C6C6
}
RuneLite Decorations Watermark {
kind=Light
colorUltraLight=#1e1e1e
colorExtraLight=#1e1e1e
colorLight=#1e1e1e
colorMid=#1e1e1e
colorDark=#1e1e1e
colorUltraDark=#1e1e1e
colorForeground=#1e1e1e
}
RuneLite Separator {
kind=Dark
colorUltraLight=#232323
colorExtraLight=#232323
colorLight=#232323
colorMid=#232323
colorDark=#232323
colorUltraDark=#232323
colorForeground=#232323
}
RuneLite Decorations Separator {
kind=Dark
colorUltraLight=#232323
colorExtraLight=#232323
colorLight=#232323
colorMid=#232323
colorDark=#232323
colorUltraDark=#232323
colorForeground=#232323
}
RuneLite Header Watermark {
kind=Dark
colorUltraLight=#1e1e1e
colorExtraLight=#1e1e1e
colorLight=#1e1e1e
colorMid=#1e1e1e
colorDark=#1e1e1e
colorUltraDark=#1e1e1e
colorForeground=#C6C6C6
}
RuneLite Header Border {
kind=Dark
colorUltraLight=#1e1e1e
colorExtraLight=#1e1e1e
colorLight=#1e1e1e
colorMid=#1e1e1e
colorDark=#1e1e1e
colorUltraDark=#1e1e1e
colorForeground=#C6C6C6
}

View File

@@ -26,12 +26,39 @@ package net.runelite.client.util;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Test;
public class StackFormatterTest
{
@Before
public void setUp()
{
Locale.setDefault(Locale.ENGLISH);
}
@Test
public void quantityToRSDecimalStackSize()
{
assertEquals("0", StackFormatter.quantityToRSDecimalStack(0));
assertEquals("8500", StackFormatter.quantityToRSDecimalStack(8_500));
assertEquals("10K", StackFormatter.quantityToRSDecimalStack(10_000));
assertEquals("21,7K", StackFormatter.quantityToRSDecimalStack(21_700));
assertEquals("100K", StackFormatter.quantityToRSDecimalStack(100_000));
assertEquals("100,3K", StackFormatter.quantityToRSDecimalStack(100_300));
assertEquals("1M", StackFormatter.quantityToRSDecimalStack(1_000_000));
assertEquals("8,4M", StackFormatter.quantityToRSDecimalStack(8_450_000));
assertEquals("10M", StackFormatter.quantityToRSDecimalStack(10_000_000));
assertEquals("12,8M", StackFormatter.quantityToRSDecimalStack(12_800_000));
assertEquals("100M", StackFormatter.quantityToRSDecimalStack(100_000_000));
assertEquals("250,1M", StackFormatter.quantityToRSDecimalStack(250_100_000));
assertEquals("1B", StackFormatter.quantityToRSDecimalStack(1_000_000_000));
assertEquals("1,5B", StackFormatter.quantityToRSDecimalStack(1500_000_000));
assertEquals("2,1B", StackFormatter.quantityToRSDecimalStack(Integer.MAX_VALUE));
}
@Test
public void quantityToRSStackSize()