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, Seth <https://github.com/sethtroll>
|
||||||
|
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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 java.awt.event.MouseEvent;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.border.CompoundBorder;
|
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
import javax.swing.border.LineBorder;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.runelite.client.ui.ColorScheme;
|
||||||
import net.runelite.client.game.AsyncBufferedImage;
|
import net.runelite.client.game.AsyncBufferedImage;
|
||||||
import net.runelite.client.util.LinkBrowser;
|
import net.runelite.client.util.LinkBrowser;
|
||||||
import net.runelite.client.util.StackFormatter;
|
import net.runelite.client.util.StackFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This panel displays an individual item result in the
|
||||||
|
* Grand Exchange search plugin.
|
||||||
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
class GrandExchangeItemPanel extends JPanel
|
class GrandExchangeItemPanel extends JPanel
|
||||||
{
|
{
|
||||||
@@ -52,7 +56,8 @@ class GrandExchangeItemPanel extends JPanel
|
|||||||
layout.setHgap(5);
|
layout.setHgap(5);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
setToolTipText(name);
|
setToolTipText(name);
|
||||||
|
setBackground(ColorScheme.MEDIUM_GRAY_COLOR);
|
||||||
|
|
||||||
Color background = getBackground();
|
Color background = getBackground();
|
||||||
|
|
||||||
addMouseListener(new MouseAdapter()
|
addMouseListener(new MouseAdapter()
|
||||||
@@ -60,7 +65,7 @@ class GrandExchangeItemPanel extends JPanel
|
|||||||
@Override
|
@Override
|
||||||
public void mouseEntered(MouseEvent e)
|
public void mouseEntered(MouseEvent e)
|
||||||
{
|
{
|
||||||
setBackground(getBackground().darker().darker());
|
setBackground(getBackground().brighter());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -76,11 +81,7 @@ class GrandExchangeItemPanel extends JPanel
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setBorder(new CompoundBorder
|
setBorder(new EmptyBorder(5, 5, 5, 0));
|
||||||
(
|
|
||||||
new LineBorder(getBackground().brighter(), 1),
|
|
||||||
new EmptyBorder(5, 5, 5, 5)
|
|
||||||
));
|
|
||||||
|
|
||||||
// Icon
|
// Icon
|
||||||
JLabel itemIcon = new JLabel();
|
JLabel itemIcon = new JLabel();
|
||||||
@@ -97,6 +98,9 @@ class GrandExchangeItemPanel extends JPanel
|
|||||||
|
|
||||||
// Item name
|
// Item name
|
||||||
JLabel itemName = new JLabel();
|
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);
|
itemName.setText(name);
|
||||||
rightPanel.add(itemName);
|
rightPanel.add(itemName);
|
||||||
|
|
||||||
@@ -110,13 +114,13 @@ class GrandExchangeItemPanel extends JPanel
|
|||||||
{
|
{
|
||||||
gePriceLabel.setText("N/A");
|
gePriceLabel.setText("N/A");
|
||||||
}
|
}
|
||||||
gePriceLabel.setForeground(Color.GREEN);
|
gePriceLabel.setForeground(ColorScheme.GRAND_EXCHANGE_PRICE);
|
||||||
rightPanel.add(gePriceLabel);
|
rightPanel.add(gePriceLabel);
|
||||||
|
|
||||||
// Alch price
|
// Alch price
|
||||||
JLabel haPriceLabel = new JLabel();
|
JLabel haPriceLabel = new JLabel();
|
||||||
haPriceLabel.setText(StackFormatter.formatNumber(haPrice.intValue()) + " alch");
|
haPriceLabel.setText(StackFormatter.formatNumber(haPrice.intValue()) + " alch");
|
||||||
haPriceLabel.setForeground(Color.orange);
|
haPriceLabel.setForeground(ColorScheme.GRAND_EXCHANGE_ALCH);
|
||||||
rightPanel.add(haPriceLabel);
|
rightPanel.add(haPriceLabel);
|
||||||
|
|
||||||
add(rightPanel, BorderLayout.CENTER);
|
add(rightPanel, BorderLayout.CENTER);
|
||||||
@@ -131,4 +135,4 @@ class GrandExchangeItemPanel extends JPanel
|
|||||||
|
|
||||||
LinkBrowser.browse(url);
|
LinkBrowser.browse(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, SomeoneWithAnInternetConnection
|
* Copyright (c) 2018, SomeoneWithAnInternetConnection
|
||||||
|
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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.BorderLayout;
|
||||||
import java.awt.CardLayout;
|
import java.awt.CardLayout;
|
||||||
import java.awt.Color;
|
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.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.BorderFactory;
|
import javax.imageio.ImageIO;
|
||||||
import javax.swing.Box;
|
|
||||||
import javax.swing.BoxLayout;
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JProgressBar;
|
import javax.swing.border.EmptyBorder;
|
||||||
import javax.swing.border.TitledBorder;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.runelite.api.GrandExchangeOffer;
|
import net.runelite.api.GrandExchangeOffer;
|
||||||
import net.runelite.api.GrandExchangeOfferState;
|
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 static net.runelite.api.GrandExchangeOfferState.EMPTY;
|
||||||
import net.runelite.api.ItemComposition;
|
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.StackFormatter;
|
||||||
|
import net.runelite.client.util.SwingUtil;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class GrandExchangeOfferSlot extends JPanel
|
public class GrandExchangeOfferSlot extends JPanel
|
||||||
{
|
{
|
||||||
private static final Color GE_INPROGRESS_ORANGE = new Color(0xd8, 0x80, 0x20).brighter();
|
private static final String FACE_CARD = "FACE_CARD";
|
||||||
private static final Color GE_FINISHED_GREEN = new Color(0, 0x5f, 0);
|
private static final String DETAILS_CARD = "DETAILS_CARD";
|
||||||
private static final Color GE_CANCELLED_RED = new Color(0x8f, 0, 0);
|
|
||||||
|
|
||||||
private static final String INFO_CARD = "INFO_CARD";
|
private static final ImageIcon RIGHT_ARROW_ICON;
|
||||||
private static final String EMPTY_CARD = "EMPTY_CARD";
|
private static final ImageIcon LEFT_ARROW_ICON;
|
||||||
|
|
||||||
|
private final JPanel container = new JPanel();
|
||||||
private final CardLayout cardLayout = new CardLayout();
|
private final CardLayout cardLayout = new CardLayout();
|
||||||
|
|
||||||
private final JLabel itemIcon = new JLabel();
|
private final JLabel itemIcon = new JLabel();
|
||||||
private final TitledBorder itemName = BorderFactory.createTitledBorder("Nothing");
|
private final JLabel itemName = new JLabel();
|
||||||
private final JLabel offerState = new JLabel("Text so the label has height");
|
private final JLabel offerInfo = new JLabel();
|
||||||
private final JProgressBar progressBar = new JProgressBar();
|
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
|
* This (sub)panel is used for each GE slot displayed
|
||||||
@@ -72,123 +108,188 @@ public class GrandExchangeOfferSlot extends JPanel
|
|||||||
|
|
||||||
private void buildPanel()
|
private void buildPanel()
|
||||||
{
|
{
|
||||||
setBorder(BorderFactory.createCompoundBorder(
|
setLayout(new BorderLayout());
|
||||||
// Add a margin underneath each slot panel to space them out
|
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||||
BorderFactory.createEmptyBorder(0, 0, 3, 0),
|
setBorder(new EmptyBorder(7, 0, 0, 0));
|
||||||
itemName
|
|
||||||
));
|
|
||||||
|
|
||||||
// The default border color is kind of dark, so we change it to something lighter
|
final MouseListener ml = new MouseAdapter()
|
||||||
itemName.setBorder(BorderFactory.createLineBorder(getBackground().brighter()));
|
{
|
||||||
|
@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
|
container.setLayout(cardLayout);
|
||||||
JPanel infoCard = new JPanel();
|
container.setBackground(ColorScheme.MEDIUM_GRAY_COLOR);
|
||||||
add(infoCard, INFO_CARD);
|
|
||||||
// Add padding to give the icon and progress bar room to breathe
|
|
||||||
infoCard.setBorder(BorderFactory.createEmptyBorder(0, 2, 2, 2));
|
|
||||||
|
|
||||||
infoCard.setLayout(new BoxLayout(infoCard, BoxLayout.X_AXIS));
|
JPanel faceCard = new JPanel();
|
||||||
// Icon on the left
|
faceCard.setOpaque(false);
|
||||||
infoCard.add(itemIcon);
|
faceCard.setLayout(new BorderLayout());
|
||||||
|
faceCard.addMouseListener(ml);
|
||||||
|
|
||||||
// Info on the right
|
itemIcon.setVerticalAlignment(JLabel.CENTER);
|
||||||
JPanel offerStatePanel = new JPanel();
|
itemIcon.setHorizontalAlignment(JLabel.CENTER);
|
||||||
offerStatePanel.setLayout(new BoxLayout(offerStatePanel, BoxLayout.Y_AXIS));
|
itemIcon.setPreferredSize(new Dimension(45, 45));
|
||||||
offerStatePanel.add(offerState);
|
|
||||||
offerStatePanel.add(progressBar);
|
|
||||||
infoCard.add(offerStatePanel);
|
|
||||||
|
|
||||||
// Card for when the slot is empty
|
itemName.setForeground(Color.WHITE);
|
||||||
JPanel emptySlotCard = new JPanel();
|
itemName.setVerticalAlignment(JLabel.BOTTOM);
|
||||||
add(emptySlotCard, EMPTY_CARD);
|
itemName.setFont(FontManager.getRunescapeSmallFont());
|
||||||
// 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());
|
|
||||||
|
|
||||||
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)
|
void updateOffer(ItemComposition offerItem, BufferedImage itemImage, @Nullable GrandExchangeOffer newOffer)
|
||||||
{
|
{
|
||||||
if (newOffer == null || newOffer.getState() == EMPTY)
|
if (newOffer == null || newOffer.getState() == EMPTY)
|
||||||
{
|
{
|
||||||
cardLayout.show(this, EMPTY_CARD);
|
return;
|
||||||
itemName.setTitle("Nothing");
|
|
||||||
}
|
}
|
||||||
else
|
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;
|
boolean buying = newOffer.getState() == GrandExchangeOfferState.BOUGHT
|
||||||
ImageIcon newItemIcon = new ImageIcon(itemImage);
|
|| newOffer.getState() == GrandExchangeOfferState.BUYING
|
||||||
itemIcon.setIcon(newItemIcon);
|
|| newOffer.getState() == GrandExchangeOfferState.CANCELLED_BUY;
|
||||||
|
|
||||||
offerState.setText(getNameForState(newOffer.getState())
|
String offerState = (buying ? "Bought " : "Sold ")
|
||||||
+ " at "
|
+ StackFormatter.quantityToRSDecimalStack(newOffer.getQuantitySold()) + " / "
|
||||||
+ StackFormatter.formatNumber(newOffer.getState() == GrandExchangeOfferState.BOUGHT ? (newOffer.getSpent() / newOffer.getTotalQuantity()) : newOffer.getPrice())
|
+ StackFormatter.quantityToRSDecimalStack(newOffer.getTotalQuantity());
|
||||||
+ (newOffer.getTotalQuantity() > 1 ? " gp ea" : " gp"));
|
|
||||||
|
|
||||||
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.setValue(newOffer.getQuantitySold());
|
||||||
progressBar.setBackground(getColorForState(newOffer.getState()));
|
progressBar.update();
|
||||||
progressBar.setString(newOffer.getQuantitySold() + "/" + newOffer.getTotalQuantity());
|
|
||||||
|
/* 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();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getNameForState(GrandExchangeOfferState state)
|
private String htmlTooltip(String value)
|
||||||
{
|
{
|
||||||
switch (state)
|
return "<html><body style = 'color:" + SwingUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR) + "'>Progress: <span style = 'color:white'>" + value + "</span></body></html>";
|
||||||
{
|
|
||||||
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";
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color getColorForState(GrandExchangeOfferState state)
|
private String htmlLabel(String key, String value)
|
||||||
{
|
{
|
||||||
switch (state)
|
return "<html><body style = 'color:white'>" + key + "<span style = 'color:" + SwingUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR) + "'>" + value + "</span></body></html>";
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, SomeoneWithAnInternetConnection
|
||||||
|
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -26,58 +27,58 @@
|
|||||||
package net.runelite.client.plugins.grandexchange;
|
package net.runelite.client.plugins.grandexchange;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.swing.BoxLayout;
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JTabbedPane;
|
import javax.swing.border.EmptyBorder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.runelite.api.Client;
|
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.game.ItemManager;
|
||||||
|
import net.runelite.client.ui.ColorScheme;
|
||||||
import net.runelite.client.ui.PluginPanel;
|
import net.runelite.client.ui.PluginPanel;
|
||||||
|
import net.runelite.client.ui.components.materialtabs.MaterialTab;
|
||||||
|
import net.runelite.client.ui.components.materialtabs.MaterialTabGroup;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
class GrandExchangePanel extends PluginPanel
|
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
|
@Getter
|
||||||
private GrandExchangeSearchPanel searchPanel;
|
private GrandExchangeSearchPanel searchPanel;
|
||||||
|
@Getter
|
||||||
private GrandExchangeOfferSlot[] offerSlotPanels = new GrandExchangeOfferSlot[MAX_OFFERS];
|
private GrandExchangeOffersPanel offersPanel;
|
||||||
|
|
||||||
private JPanel offerPanel = new JPanel();
|
|
||||||
|
|
||||||
private JTabbedPane tabbedPane = new JTabbedPane();
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GrandExchangePanel(Client client, ItemManager itemManager, ScheduledExecutorService executor)
|
GrandExchangePanel(Client client, ItemManager itemManager, ScheduledExecutorService executor)
|
||||||
{
|
{
|
||||||
setLayout(new BorderLayout());
|
super(false);
|
||||||
add(tabbedPane, BorderLayout.NORTH);
|
|
||||||
|
|
||||||
// Offer Panel
|
setLayout(new BorderLayout());
|
||||||
offerPanel.setLayout(new BoxLayout(offerPanel, BoxLayout.Y_AXIS));
|
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||||
for (int i = 0; i < offerSlotPanels.length; ++i)
|
|
||||||
{
|
|
||||||
offerSlotPanels[i] = new GrandExchangeOfferSlot();
|
|
||||||
offerPanel.add(offerSlotPanels[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search Panel
|
// Search Panel
|
||||||
searchPanel = new GrandExchangeSearchPanel(client, itemManager, executor);
|
searchPanel = new GrandExchangeSearchPanel(client, itemManager, executor);
|
||||||
|
|
||||||
tabbedPane.addTab("Offers", offerPanel);
|
//Offers Panel
|
||||||
tabbedPane.addTab("Search", searchPanel);
|
offersPanel = new GrandExchangeOffersPanel(client, itemManager, executor);
|
||||||
}
|
|
||||||
|
|
||||||
void updateOffer(ItemComposition item, BufferedImage itemImage, GrandExchangeOffer newOffer, int slot)
|
MaterialTab offersTab = new MaterialTab("Offers", tabGroup, offersPanel);
|
||||||
{
|
searchTab = new MaterialTab("Search", tabGroup, searchPanel);
|
||||||
offerSlotPanels[slot].updateOffer(item, itemImage, newOffer);
|
|
||||||
|
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()
|
void showSearch()
|
||||||
@@ -87,7 +88,7 @@ class GrandExchangePanel extends PluginPanel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tabbedPane.setSelectedComponent(searchPanel);
|
tabGroup.select(searchTab);
|
||||||
revalidate();
|
revalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,7 @@ import net.runelite.api.ItemComposition;
|
|||||||
import net.runelite.api.MenuEntry;
|
import net.runelite.api.MenuEntry;
|
||||||
import net.runelite.api.events.ConfigChanged;
|
import net.runelite.api.events.ConfigChanged;
|
||||||
import net.runelite.api.events.FocusChanged;
|
import net.runelite.api.events.FocusChanged;
|
||||||
|
import net.runelite.api.events.GameStateChanged;
|
||||||
import net.runelite.api.events.GrandExchangeOfferChanged;
|
import net.runelite.api.events.GrandExchangeOfferChanged;
|
||||||
import net.runelite.api.events.MenuEntryAdded;
|
import net.runelite.api.events.MenuEntryAdded;
|
||||||
import net.runelite.api.widgets.WidgetID;
|
import net.runelite.api.widgets.WidgetID;
|
||||||
@@ -161,7 +162,16 @@ public class GrandExchangePlugin extends Plugin
|
|||||||
ItemComposition offerItem = itemManager.getItemComposition(offer.getItemId());
|
ItemComposition offerItem = itemManager.getItemComposition(offer.getItemId());
|
||||||
boolean shouldStack = offerItem.isStackable() || offer.getTotalQuantity() > 1;
|
boolean shouldStack = offerItem.isStackable() || offer.getTotalQuantity() > 1;
|
||||||
BufferedImage itemImage = itemManager.getImage(offer.getItemId(), offer.getTotalQuantity(), shouldStack);
|
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
|
@Subscribe
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, Seth <https://github.com/sethtroll>
|
* Copyright (c) 2018, Seth <https://github.com/sethtroll>
|
||||||
|
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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 com.google.common.base.Strings;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.CardLayout;
|
||||||
import java.awt.GridLayout;
|
import java.awt.Dimension;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.GridBagConstraints;
|
||||||
|
import java.awt.GridBagLayout;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -36,8 +38,8 @@ import java.util.concurrent.ExecutionException;
|
|||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -45,26 +47,69 @@ import net.runelite.api.Client;
|
|||||||
import net.runelite.api.ItemComposition;
|
import net.runelite.api.ItemComposition;
|
||||||
import net.runelite.client.game.AsyncBufferedImage;
|
import net.runelite.client.game.AsyncBufferedImage;
|
||||||
import net.runelite.client.game.ItemManager;
|
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.IconTextField;
|
||||||
|
import net.runelite.client.ui.components.PluginErrorPanel;
|
||||||
import net.runelite.http.api.item.Item;
|
import net.runelite.http.api.item.Item;
|
||||||
import net.runelite.http.api.item.ItemPrice;
|
import net.runelite.http.api.item.ItemPrice;
|
||||||
import net.runelite.http.api.item.SearchResult;
|
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
|
@Slf4j
|
||||||
class GrandExchangeSearchPanel extends JPanel
|
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 Client client;
|
||||||
private final ItemManager itemManager;
|
private final ItemManager itemManager;
|
||||||
private final ScheduledExecutorService executor;
|
private final ScheduledExecutorService executor;
|
||||||
|
|
||||||
private ImageIcon search;
|
private final IconTextField searchBox = new IconTextField();
|
||||||
|
|
||||||
private IconTextField searchBox = new IconTextField();
|
/* The main container, this holds the search bar and the center panel */
|
||||||
private JPanel container = new JPanel();
|
private final JPanel container = new JPanel();
|
||||||
private JPanel searchItemsPanel = new JPanel();
|
|
||||||
private JLabel searchingLabel = new JLabel();
|
/* 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)
|
GrandExchangeSearchPanel(Client client, ItemManager itemManager, ScheduledExecutorService executor)
|
||||||
{
|
{
|
||||||
@@ -77,38 +122,54 @@ class GrandExchangeSearchPanel extends JPanel
|
|||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
container.setLayout(new BorderLayout());
|
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||||
|
|
||||||
// Search Box
|
container.setLayout(new BorderLayout(5, 5));
|
||||||
try
|
container.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||||
{
|
container.setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)));
|
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(searchBox, BorderLayout.NORTH);
|
||||||
|
container.add(centerPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
// Searching label
|
add(container, BorderLayout.CENTER);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void priceLookup(String item)
|
void priceLookup(String item)
|
||||||
@@ -129,7 +190,9 @@ class GrandExchangeSearchPanel extends JPanel
|
|||||||
|
|
||||||
// Input is not empty, add searching label
|
// Input is not empty, add searching label
|
||||||
searchItemsPanel.removeAll();
|
searchItemsPanel.removeAll();
|
||||||
showSearchString("Searching...");
|
searchBox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR);
|
||||||
|
searchBox.setEditable(false);
|
||||||
|
searchBox.setIcon(LOADING_ICON);
|
||||||
|
|
||||||
SearchResult result;
|
SearchResult result;
|
||||||
|
|
||||||
@@ -140,13 +203,19 @@ class GrandExchangeSearchPanel extends JPanel
|
|||||||
catch (ExecutionException ex)
|
catch (ExecutionException ex)
|
||||||
{
|
{
|
||||||
log.warn("Unable to search for item {}", lookup, 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itemsList.clear();
|
||||||
|
|
||||||
if (result != null && !result.getItems().isEmpty())
|
if (result != null && !result.getItems().isEmpty())
|
||||||
{
|
{
|
||||||
|
cardLayout.show(centerPanel, RESULTS_PANEL);
|
||||||
|
|
||||||
for (Item item : result.getItems())
|
for (Item item : result.getItems())
|
||||||
{
|
{
|
||||||
int itemId = item.getId();
|
int itemId = item.getId();
|
||||||
@@ -169,7 +238,7 @@ class GrandExchangeSearchPanel extends JPanel
|
|||||||
|
|
||||||
AsyncBufferedImage itemImage = itemManager.getImage(itemId);
|
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 using hotkey to lookup item, stop after finding match.
|
||||||
if (exactMatch && item.getName().equalsIgnoreCase(lookup))
|
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(() ->
|
SwingUtilities.invokeLater(() ->
|
||||||
{
|
{
|
||||||
if (ITEMS_LIST.isEmpty())
|
int index = 0;
|
||||||
|
for (GrandExchangeItems item : itemsList)
|
||||||
{
|
{
|
||||||
showSearchString("No results found.");
|
GrandExchangeItemPanel panel = new GrandExchangeItemPanel(item.getIcon(), item.getName(),
|
||||||
}
|
item.getItemId(), item.getGePrice(), item.getHaPrice());
|
||||||
else
|
|
||||||
{
|
/*
|
||||||
for (GrandExchangeItems item : ITEMS_LIST)
|
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(),
|
JPanel marginWrapper = new JPanel(new BorderLayout());
|
||||||
item.getItemId(), item.getGePrice(), item.getHaPrice());
|
marginWrapper.setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||||
|
marginWrapper.setBorder(new EmptyBorder(5, 0, 0, 0));
|
||||||
searchItemsPanel.add(panel);
|
marginWrapper.add(panel, BorderLayout.NORTH);
|
||||||
|
searchItemsPanel.add(marginWrapper, constraints);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
searchItemsPanel.add(panel, constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints.gridy++;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove searching label after search is complete
|
// remove focus from the search bar
|
||||||
showSearchString(null);
|
searchItemsPanel.requestFocusInWindow();
|
||||||
ITEMS_LIST.clear();
|
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()
|
public PluginErrorPanel()
|
||||||
{
|
{
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
setBorder(new EmptyBorder(50, 0, 0, 0));
|
setBorder(new EmptyBorder(50, 10, 0, 10));
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
|
|
||||||
noResultsTitle.setForeground(Color.WHITE);
|
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
|
* Converts a string representation of a stack
|
||||||
* back to (close to) it's original value.
|
* back to (close to) it's original value.
|
||||||
@@ -147,8 +202,8 @@ public class StackFormatter
|
|||||||
*
|
*
|
||||||
* @param number the long number to format
|
* @param number the long number to format
|
||||||
* @return the formatted String
|
* @return the formatted String
|
||||||
* @exception ArithmeticException if rounding is needed with rounding
|
* @throws ArithmeticException if rounding is needed with rounding
|
||||||
* mode being set to RoundingMode.UNNECESSARY
|
* mode being set to RoundingMode.UNNECESSARY
|
||||||
* @see java.text.Format#format
|
* @see java.text.Format#format
|
||||||
*/
|
*/
|
||||||
public static String formatNumber(final long number)
|
public static String formatNumber(final long number)
|
||||||
@@ -161,8 +216,8 @@ public class StackFormatter
|
|||||||
*
|
*
|
||||||
* @param number the double number to format
|
* @param number the double number to format
|
||||||
* @return the formatted String
|
* @return the formatted String
|
||||||
* @exception ArithmeticException if rounding is needed with rounding
|
* @throws ArithmeticException if rounding is needed with rounding
|
||||||
* mode being set to RoundingMode.UNNECESSARY
|
* mode being set to RoundingMode.UNNECESSARY
|
||||||
* @see java.text.Format#format
|
* @see java.text.Format#format
|
||||||
*/
|
*/
|
||||||
public static String formatNumber(double number)
|
public static String formatNumber(double number)
|
||||||
|
|||||||
@@ -94,6 +94,14 @@ public class SwingUtil
|
|||||||
System.setProperty("sun.awt.noerasebackground", "true");
|
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
|
* 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.NumberFormat;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
|
import java.util.Locale;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class StackFormatterTest
|
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
|
@Test
|
||||||
public void quantityToRSStackSize()
|
public void quantityToRSStackSize()
|
||||||
|
|||||||
Reference in New Issue
Block a user