Grand Exchange Plugin redesign
General: - Applied the design I proposed in issue #1342 - Applied custom component: MaterialTabs - Removed default scrolling behaviour from parent PluginPanel - Added error panels for empty searches and empty offer slots - Added new formatter to the StackFormatter that displays integers as rs stacks with decimals (21700 into 21.7k) - Changed the Locale on the stack formatter and respective unit testing to UK, this makes sure all tests are consistent with Travis (ex: i ran the unit testing in europe, travis ran in the us, so it passed my tests, failed his) Offers: - Refactored the GE offers into it's own seperate file: GrandExchangeOffersPanel - Redesigned the ge offers items - Included the custom component ThinProgressBar on the bottom of each ge item panel - Added secondary information panel, toggled by clicking on the primary panel - Added a game state check that resets all ge offers on logout Search: - Recoloured and resized the search bar - Added new icons to the search bar (incluing a loading wheel gif) - Removed focus on the search bar when results are displayed - Added custom scrolling behaviour - Blocked input when search is in progress
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -161,7 +162,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
|
||||
|
||||
@@ -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,9 +27,10 @@ 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;
|
||||
@@ -36,8 +38,8 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.imageio.ImageIO;
|
||||
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;
|
||||
@@ -45,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.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 ImageIcon 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)
|
||||
{
|
||||
@@ -77,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)
|
||||
@@ -129,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;
|
||||
|
||||
@@ -140,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();
|
||||
@@ -169,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))
|
||||
@@ -178,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ public class PluginErrorPanel extends JPanel
|
||||
public PluginErrorPanel()
|
||||
{
|
||||
setOpaque(false);
|
||||
setBorder(new EmptyBorder(50, 0, 0, 0));
|
||||
setBorder(new EmptyBorder(50, 10, 0, 10));
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
noResultsTitle.setForeground(Color.WHITE);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -94,6 +94,14 @@ public class SwingUtil
|
||||
System.setProperty("sun.awt.noerasebackground", "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
||||
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: 388 B |
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user