From e56e559ecdbf1e7da96d7be18c3ef17a89753613 Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Sun, 22 Apr 2018 03:47:52 +0100 Subject: [PATCH] Added custom components + moved/rewrote some To organize the project a bit, decided to add a new folder inside the ui folder, to hold all custom components. ui/components/ Rewrote IconTextField I wasn't happy with its functionality, so I rewrote it to include the following: - Left aligned centered icons - Animated gif support (ex: loading wheels) - Custom hover effects (color change) - Input blocking All changes: - Created new folder ui/components/ - Moved JShadowLabel and JShadowLabelUI to components folder - Moved IconTextField to components folder - Rewrote IconTextField - Created new components: MaterialTab & MaterialTabGroup - Created new components: CustomScrollBarUI - Created new components: PluginErrorPanel - Created new components: ThinProgressBar - Applied the new scroll bar ui to the UI defaults (UIManager) --- .../GrandExchangeSearchPanel.java | 5 +- .../client/plugins/hiscore/HiscorePanel.java | 22 +-- .../plugins/skillcalculator/UIActionSlot.java | 2 +- .../skillcalculator/UICombinedActionSlot.java | 2 +- .../client/plugins/xptracker/XpInfoBox.java | 2 +- .../net/runelite/client/ui/ColorScheme.java | 3 + .../net/runelite/client/ui/PluginPanel.java | 1 - .../ui/components/CustomScrollBarUI.java | 103 ++++++++++++ .../client/ui/components/IconTextField.java | 152 ++++++++++++++++++ .../ui/components/PluginErrorPanel.java | 75 +++++++++ .../components/ThinProgressBar.java} | 95 ++++++----- .../components/materialtabs/MaterialTab.java | 104 ++++++++++++ .../materialtabs/MaterialTabGroup.java | 102 ++++++++++++ .../shadowlabel}/JShadowedLabel.java | 2 +- .../shadowlabel}/JShadowedLabelUI.java | 2 +- .../net/runelite/client/util/SwingUtil.java | 2 + .../runelite/client/ui/components/error.png | Bin 0 -> 15599 bytes .../client/ui/components/loading_spinner.gif | Bin 0 -> 32397 bytes .../ui/components/loading_spinner_darker.gif | Bin 0 -> 23668 bytes .../runelite/client/ui/components/search.png | Bin 0 -> 16259 bytes .../client/ui/components/search_darker.png | Bin 0 -> 16280 bytes 21 files changed, 615 insertions(+), 59 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/components/CustomScrollBarUI.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/components/PluginErrorPanel.java rename runelite-client/src/main/java/net/runelite/client/{plugins/hiscore/IconTextField.java => ui/components/ThinProgressBar.java} (52%) create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTab.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java rename runelite-client/src/main/java/net/runelite/client/ui/{ => components/shadowlabel}/JShadowedLabel.java (97%) rename runelite-client/src/main/java/net/runelite/client/ui/{ => components/shadowlabel}/JShadowedLabelUI.java (97%) create mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/components/error.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/components/loading_spinner.gif create mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/components/loading_spinner_darker.gif create mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/components/search.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/components/search_darker.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java index d3eb52044d..6372ea82c6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java @@ -35,7 +35,6 @@ import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; import javax.imageio.ImageIO; -import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; @@ -46,7 +45,7 @@ import net.runelite.api.Client; import net.runelite.api.ItemComposition; import net.runelite.client.game.AsyncBufferedImage; import net.runelite.client.game.ItemManager; -import net.runelite.client.plugins.hiscore.IconTextField; +import net.runelite.client.ui.components.IconTextField; import net.runelite.http.api.item.Item; import net.runelite.http.api.item.ItemPrice; import net.runelite.http.api.item.SearchResult; @@ -60,7 +59,7 @@ class GrandExchangeSearchPanel extends JPanel private final ItemManager itemManager; private final ScheduledExecutorService executor; - private Icon search; + private ImageIcon search; private IconTextField searchBox = new IconTextField(); private JPanel container = new JPanel(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePanel.java index 948b2dd8ee..3bf64d811c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePanel.java @@ -49,7 +49,6 @@ import javax.imageio.ImageIO; import javax.inject.Inject; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; -import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; @@ -64,6 +63,7 @@ import net.runelite.api.Client; import net.runelite.api.Experience; import net.runelite.api.Player; import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.IconTextField; import net.runelite.client.util.StackFormatter; import net.runelite.http.api.hiscore.HiscoreClient; import net.runelite.http.api.hiscore.HiscoreEndpoint; @@ -165,7 +165,7 @@ public class HiscorePanel extends PluginPanel inputPanel.setLayout(new BorderLayout(7, 7)); inputPanel.setBorder(subPanelBorder); - Icon search; + ImageIcon search; try { BufferedImage icon; @@ -417,14 +417,14 @@ public class HiscorePanel extends PluginPanel { String rank = (result.getBountyHunterHunter().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getBountyHunterHunter().getRank()); text = "Bounty Hunter - Hunter Kills" + System.lineSeparator() - + "Rank: " + rank; + + "Rank: " + rank; break; } case "Last Man Standing": { String rank = (result.getLastManStanding().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getLastManStanding().getRank()); text = "Last Man Standing" + System.lineSeparator() - + "Rank: " + rank; + + "Rank: " + rank; break; } case "Overall": @@ -586,13 +586,13 @@ public class HiscorePanel extends PluginPanel if (result.getPlayer() != null) { int combatLevel = Experience.getCombatLevel( - result.getAttack().getLevel(), - result.getStrength().getLevel(), - result.getDefence().getLevel(), - result.getHitpoints().getLevel(), - result.getMagic().getLevel(), - result.getRanged().getLevel(), - result.getPrayer().getLevel() + result.getAttack().getLevel(), + result.getStrength().getLevel(), + result.getDefence().getLevel(), + result.getHitpoints().getLevel(), + result.getMagic().getLevel(), + result.getRanged().getLevel(), + result.getPrayer().getLevel() ); label.setText(Integer.toString(combatLevel)); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java index 74c3bfe16f..dcc6cba345 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java @@ -34,7 +34,7 @@ import javax.swing.JLabel; import javax.swing.JPanel; import net.runelite.client.plugins.skillcalculator.beans.SkillDataEntry; import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.JShadowedLabel; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; class UIActionSlot extends JPanel { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java index acdd05b7de..d981e9d97f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java @@ -33,7 +33,7 @@ import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JPanel; import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.JShadowedLabel; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; class UICombinedActionSlot extends JPanel { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java index 01bcdcc0bd..4ece97d152 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java @@ -53,7 +53,7 @@ import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.Skill; import net.runelite.client.game.SkillIconManager; -import net.runelite.client.ui.JShadowedLabel; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; import net.runelite.client.util.LinkBrowser; import org.pushingpixels.substance.internal.SubstanceSynapse; diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java b/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java index 1f5f9d7878..bec0c4f052 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java @@ -34,6 +34,9 @@ public class ColorScheme /* The orange color used for the branding's accents */ public static final Color BRAND_ORANGE = new Color(220, 138, 0); + /* The orange color used for the branding's accents, with lowered opacity */ + public static final Color BRAND_ORANGE_TRANSPARENT = new Color(220, 138, 0, 120); + public static final Color DARKER_GRAY_COLOR = new Color(30, 30, 30); public static final Color DARK_GRAY_COLOR = new Color(40, 40, 40); public static final Color MEDIUM_GRAY_COLOR = new Color(77, 77, 77); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java index ca93c74fe0..44835b771e 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java @@ -68,7 +68,6 @@ public abstract class PluginPanel extends JPanel northPanel.setBackground(ColorScheme.DARK_GRAY_COLOR); scrollPane = new JScrollPane(northPanel); - scrollPane.getVerticalScrollBar().setUnitIncrement(16); //Otherwise scrollspeed is really slow scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); wrappedPanel = new JPanel(); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/CustomScrollBarUI.java b/runelite-client/src/main/java/net/runelite/client/ui/components/CustomScrollBarUI.java new file mode 100644 index 0000000000..7714042049 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/CustomScrollBarUI.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018, Psikoi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.components; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JScrollBar; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicScrollBarUI; +import net.runelite.client.ui.ColorScheme; + +/** + * This scroll bar UI is to be used for the "RuneLite Obsidian" client theme. + * It is a part of the client's redesign as detailed on issue #1342 + */ +public class CustomScrollBarUI extends BasicScrollBarUI +{ + /** + * Overrides the painting of the bar's track (the darker part underneath that extends + * the full page length). + */ + @Override + protected void paintTrack(Graphics graphics, JComponent jComponent, Rectangle rectangle) + { + graphics.setColor(ColorScheme.SCROLL_TRACK_COLOR); + graphics.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + } + + /** + * Overrides the painting of the bar's thumb (the lighter part on top that users + * use to slide up and down the page). + */ + @Override + protected void paintThumb(Graphics graphics, JComponent jComponent, Rectangle rectangle) + { + graphics.setColor(ColorScheme.MEDIUM_GRAY_COLOR); + graphics.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + } + + /** + * Creates an empty JButton to be used as the scroll bar's arrows (to disable the arrows). + */ + protected JButton createEmptyButton() + { + JButton button = new JButton(); + Dimension zeroDim = new Dimension(0, 0); + button.setPreferredSize(zeroDim); + button.setMinimumSize(zeroDim); + button.setMaximumSize(zeroDim); + return button; + } + + public static ComponentUI createUI(JComponent c) + { + JScrollBar bar = (JScrollBar) c; + bar.setUnitIncrement(16); + bar.setPreferredSize(new Dimension(7, 0)); + return new CustomScrollBarUI(); + } + + /** + * Applies an empty button to the decrease (down arrow) button. + */ + @Override + protected JButton createDecreaseButton(int orientation) + { + return createEmptyButton(); + } + + /** + * Applies an empty button to the increase (up arrow) button. + */ + @Override + protected JButton createIncreaseButton(int orientation) + { + return createEmptyButton(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java b/runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java new file mode 100644 index 0000000000..fde8d673cb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017, Adam + * Copyright (c) 2018, Psikoi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.components; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import net.runelite.client.ui.ColorScheme; + +/** + * This component is a JTextField with an icon on its left side. + */ +public class IconTextField extends JPanel +{ + private final JTextField textField; + + //to support gifs, the icon needs to be wrapped in a JLabel + private final JLabel iconWrapperLabel; + + //the default background color, this needs to be stored for hover effects + private Color backgroundColor = ColorScheme.DARKER_GRAY_COLOR; + //the default hover background color, this needs to be stored for hover effects + private Color hoverBackgroundColor; + + // the input can be blocked (no clicking, no editing, no hover effects) + private boolean blocked; + + public IconTextField() + { + setLayout(new BorderLayout()); + + this.iconWrapperLabel = new JLabel(); + this.iconWrapperLabel.setPreferredSize(new Dimension(30, 0)); + this.iconWrapperLabel.setVerticalAlignment(JLabel.CENTER); + this.iconWrapperLabel.setHorizontalAlignment(JLabel.CENTER); + + this.textField = new JTextField(); + this.textField.setBorder(null); + this.textField.setOpaque(false); + this.textField.setSelectedTextColor(Color.WHITE); + this.textField.setSelectionColor(ColorScheme.BRAND_ORANGE_TRANSPARENT); + + add(iconWrapperLabel, BorderLayout.WEST); + add(textField, BorderLayout.CENTER); + + textField.addMouseListener(new MouseAdapter() + { + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + if (blocked) + { + return; + } + + if (hoverBackgroundColor != null) + { + IconTextField.super.setBackground(hoverBackgroundColor); + } + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + IconTextField.super.setBackground(backgroundColor); + } + }); + } + + public void addActionListener(ActionListener actionListener) + { + textField.addActionListener(actionListener); + } + + public void setIcon(ImageIcon icon) + { + iconWrapperLabel.setIcon(icon); + } + + public String getText() + { + return textField.getText(); + } + + public void setText(String text) + { + textField.setText(text); + } + + @Override + public void setBackground(Color color) + { + if (color == null) + { + return; + } + super.setBackground(color); + this.backgroundColor = color; + } + + public void setHoverBackgroundColor(Color hoverBackgroundColor) + { + if (hoverBackgroundColor == null) + { + return; + } + this.hoverBackgroundColor = hoverBackgroundColor; + } + + public void setEditable(boolean editable) + { + this.blocked = !editable; + textField.setEditable(editable); + textField.setFocusable(editable); + if (!editable) + { + super.setBackground(backgroundColor); + } + } + +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/PluginErrorPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/components/PluginErrorPanel.java new file mode 100644 index 0000000000..779253cff0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/PluginErrorPanel.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018, Psikoi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.components; + +import java.awt.BorderLayout; +import java.awt.Color; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; + +/** + * A component to display an error/info message (to be used on a plugin panel) + * Example uses are: no ge search results found, no ge offers found. + */ +public class PluginErrorPanel extends JPanel +{ + private final JLabel noResultsTitle = new JShadowedLabel(); + private final JLabel noResultsDescription = new JShadowedLabel(); + + public PluginErrorPanel() + { + setOpaque(false); + setBorder(new EmptyBorder(50, 0, 0, 0)); + setLayout(new BorderLayout()); + + noResultsTitle.setForeground(Color.WHITE); + noResultsTitle.setHorizontalAlignment(SwingConstants.CENTER); + + noResultsDescription.setFont(FontManager.getRunescapeSmallFont()); + noResultsDescription.setForeground(Color.GRAY); + noResultsDescription.setHorizontalAlignment(SwingConstants.CENTER); + + add(noResultsTitle, BorderLayout.NORTH); + add(noResultsDescription, BorderLayout.CENTER); + + setVisible(false); + } + + /** + * Changes the content of the panel to the given parameters. + * The description has to be wrapped in html so that its text can be wrapped. + */ + public void setContent(String title, String description) + { + noResultsTitle.setText(title); + noResultsDescription.setText("" + description + ""); + setVisible(true); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/IconTextField.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ThinProgressBar.java similarity index 52% rename from runelite-client/src/main/java/net/runelite/client/plugins/hiscore/IconTextField.java rename to runelite-client/src/main/java/net/runelite/client/ui/components/ThinProgressBar.java index 055640de17..424e4ebebb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/IconTextField.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ThinProgressBar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Adam + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,54 +23,71 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.runelite.client.plugins.hiscore; +package net.runelite.client.ui.components; -import java.awt.Graphics; -import java.awt.Insets; -import javax.swing.BorderFactory; -import javax.swing.Icon; -import javax.swing.JTextField; -import javax.swing.border.Border; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import javax.swing.JPanel; +import lombok.Setter; +import net.runelite.client.ui.ColorScheme; -public class IconTextField extends JTextField +/** + * A progress bar to be displayed underneath the GE offer item panels + */ +public class ThinProgressBar extends JPanel { - private Border border; - private Icon icon; + @Setter + private int maximumValue; - @Override - public void setBorder(Border border) + @Setter + private int value; + + private final JPanel topBar = new JPanel(); + + public ThinProgressBar() { - this.border = border; + setLayout(new BorderLayout()); + setBackground(Color.GREEN.darker()); - if (icon == null) + topBar.setPreferredSize(new Dimension(100, 4)); + topBar.setBackground(ColorScheme.PROGRESS_COMPLETE_COLOR); + + add(topBar, BorderLayout.WEST); + } + + /** + * Updates the UI based on the percentage progress + */ + public void update() + { + double percentage = getPercentage(); + int topWidth = (int) (getSize().width * (percentage / 100)); + + topBar.setPreferredSize(new Dimension(topWidth, 4)); + topBar.repaint(); + + revalidate(); + repaint(); + } + + public double getPercentage() + { + if (value == 0) { - super.setBorder(border); - } - else - { - Border margin = BorderFactory.createEmptyBorder(0, icon.getIconWidth() + 4, 0, 0); - Border compound = BorderFactory.createCompoundBorder(border, margin); - super.setBorder(compound); + return 0; } + + return (value * 100) / maximumValue; } @Override - public void paintComponent(Graphics graphics) + public void setForeground(Color color) { - super.paintComponent(graphics); - - Insets iconInsets = border.getBorderInsets(this); - icon.paintIcon(this, graphics, iconInsets.left, iconInsets.top); + if (topBar != null) + { + topBar.setBackground(color); + } + setBackground(color.darker()); } - - public void setIcon(Icon icon) - { - this.icon = icon; - resetBorder(); - } - - private void resetBorder() - { - setBorder(border); - } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTab.java b/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTab.java new file mode 100644 index 0000000000..692b9891bc --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTab.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018, Psikoi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.components.materialtabs; + +import java.awt.Color; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import lombok.Getter; +import net.runelite.client.ui.ColorScheme; + +/** + * This class represents a Material Design inspired tab. + *

+ * Each tab will communicate with it's containing group when it's clicked + * and that group will display the tab's content on it's own display. + * + * @author Psikoi + */ +public class MaterialTab extends JLabel +{ + private static final Border SELECTED_BORDER = new CompoundBorder( + BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.BRAND_ORANGE), + BorderFactory.createEmptyBorder(5, 10, 5, 10)); + + private static final Border UNSELECTED_BORDER = BorderFactory + .createEmptyBorder(5, 10, 5, 10); + + /* The tab's containing group */ + private final MaterialTabGroup group; + + /* The tab's associated content display */ + @Getter + private final JComponent content; + + @Getter + private boolean selected; + + public MaterialTab(String string, MaterialTabGroup group, JComponent content) + { + super(string); + + this.group = group; + this.content = content; + + if (selected) + { + select(); + } + else + { + unselect(); + } + + addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + group.select(MaterialTab.this); + } + }); + } + + public void select() + { + setBorder(SELECTED_BORDER); + setForeground(Color.WHITE); + selected = true; + } + + public void unselect() + { + setBorder(UNSELECTED_BORDER); + setForeground(Color.GRAY); + selected = false; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java b/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java new file mode 100644 index 0000000000..5e50953523 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018, Psikoi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.components.materialtabs; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JPanel; + +/** + * This class will be a container (group) for the new Material Tabs. It will + * contain a list of tabs and a display (JPanel). When a tab is selected, the + * JPanel "display" will display the content associated with that tab. + *

+ * How to use these tabs: + *

    + *
  1. 1 - Create displays (JPanels) for each tab
  2. + *
  3. 2 - Create an empty JPanel to serve as the group's display
  4. + *
  5. 3 - Create a new MaterialGroup, passing the panel in step 2 as a param
  6. + *
  7. 4 - Create new tabs, passing the group in step 3 and one of the panels in + * step 1 as params
  8. + *
  9. 5 - Add the tabs to the group using the MaterialTabGroup#addTab method
  10. + *
  11. 6 - Select one of the tabs using the MaterialTab#select method
  12. + *
+ * + * @author Psikoi + */ +public class MaterialTabGroup extends JPanel +{ + /* The panel on which the content tab's content will be displayed on. */ + private final JPanel display; + /* A list of all the tabs contained in this group. */ + private final List tabs = new ArrayList<>(); + + public MaterialTabGroup(JPanel display) + { + this.display = display; + this.display.setLayout(new BorderLayout()); + setLayout(new FlowLayout()); + setOpaque(false); + } + + public void addTab(MaterialTab tab) + { + tabs.add(tab); + add(tab, BorderLayout.NORTH); + } + + /*** + * Selects a tab from the group, and sets the display's content to the + * tab's associated content. + * @param selectedTab - The tab to select + */ + public boolean select(MaterialTab selectedTab) + { + if (!tabs.contains(selectedTab)) + { + return false; + } + + display.removeAll(); + + for (MaterialTab tab : tabs) + { + if (tab.equals(selectedTab)) + { + tab.select(); + display.add(tab.getContent()); + display.repaint(); + } + else + { + tab.unselect(); + } + } + + return true; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/JShadowedLabel.java b/runelite-client/src/main/java/net/runelite/client/ui/components/shadowlabel/JShadowedLabel.java similarity index 97% rename from runelite-client/src/main/java/net/runelite/client/ui/JShadowedLabel.java rename to runelite-client/src/main/java/net/runelite/client/ui/components/shadowlabel/JShadowedLabel.java index 37182fb762..35ccf12374 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/JShadowedLabel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/shadowlabel/JShadowedLabel.java @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.runelite.client.ui; +package net.runelite.client.ui.components.shadowlabel; import java.awt.Color; import java.awt.Point; diff --git a/runelite-client/src/main/java/net/runelite/client/ui/JShadowedLabelUI.java b/runelite-client/src/main/java/net/runelite/client/ui/components/shadowlabel/JShadowedLabelUI.java similarity index 97% rename from runelite-client/src/main/java/net/runelite/client/ui/JShadowedLabelUI.java rename to runelite-client/src/main/java/net/runelite/client/ui/components/shadowlabel/JShadowedLabelUI.java index 0494b66d1f..32e59f4993 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/JShadowedLabelUI.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/shadowlabel/JShadowedLabelUI.java @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.runelite.client.ui; +package net.runelite.client.ui.components.shadowlabel; import java.awt.Graphics; import javax.swing.JLabel; diff --git a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java index b6a66a35d6..a83cd7e24e 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java @@ -60,6 +60,7 @@ import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE; import javax.swing.plaf.FontUIResource; import lombok.extern.slf4j.Slf4j; import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.components.CustomScrollBarUI; import org.pushingpixels.substance.internal.SubstanceSynapse; import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities; @@ -82,6 +83,7 @@ public class SwingUtil UIManager.put("Button.foreground", Color.WHITE); UIManager.put("MenuItem.foreground", Color.WHITE); + UIManager.put("ScrollBarUI", CustomScrollBarUI.class.getName()); // Do not render shadows under popups/tooltips. // Fixes black boxes under popups that are above the game applet. diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/components/error.png b/runelite-client/src/main/resources/net/runelite/client/ui/components/error.png new file mode 100644 index 0000000000000000000000000000000000000000..11048a6501f97a1494a69815d50b74a2080bd7cb GIT binary patch literal 15599 zcmeI3e{dA#8OJv&5`tkOXr$>397nYY$!7O%@8^0s5^`vgav>(MUZBmexBFhQ;coZ1 z+v6@VGAS4fql4J`Ypksm5JZImYdcQmr{ay$&R|OsiqtSTB^2BG1EqATrtjWe?k;bV zcQT{>r}N&--QM#)@ArB4{k+fn?EBAdYH7Z+xTvg%qNw7=22U%zmgwKY>*4SG%Oe83 z%m_DhL@8?K9Q|8BJ@xcliYleZ^ zRD1lvt>;@RuUivN{ItCMH^!;d{%tF#-x5D``0cyG zEni$SQoH8TxsnANHWdEiZ)e~5YvjoDr(-*XM}?BrCl&sWpZ7cebM%Hb>YW+C`jhhO zS=9OOY`%JAxbm8Fy(jkEUi?+E|H(zg%Le|TJUVDtR6DX{XW7xY?1ptO)SWHZ!ON#= zUDR`(|72<_-ukk*yyDd5x_&Bf{PTYH0>!;`-!wL+?JDSL#V& z<^jG;iY%Tr=ds&f*tV^D%gFS@{)49#KC-)~seJX%1A`xK-TnP%_fPxR2h8E*@-Hv0Snj7No|D^sn$Nq8 z7u8U;AgO*(od|^?Z;Em)PJ{(~n`zjCj`k{=kf4?5!gP?)I35rcBXU@iRmGsk z75r*Ut1%k&fwG^>yh7pZKuR>j4pL+iLYOgCv&A-RUgLl?hlS^sF+J-ybc; zjiX9(pm$19ay`e!@Ot@1C8`OE2pT-e4Ml0-hBMnZz-mP0}pV8Tk5i^(YdG4BDq7?O z2>LuSd|X8JvS+eoG^k@jVaH^`JV!>D&ZxZ8U8+>1TjALe9$T60xpiC>$rjsh@=tC? z-(*xI78wKqf;R~+JRihMa3MhOCc%a0gLnxp1PI4T z@Fu~9=Yx0&E(8ePB)IT=5HG=n0KuCC7oHE|CAbhEc$475^Fh1>7Xk!t5?pvbh?n3( zfZ$Dn3(p7f5?lxnyh(83`5<0`3ju;R2`)Sz#7l4?K=3BPh3A8K2`&T(-Xys2d=M|e zg#f{uSX@Q9H$i~{U;m85_dMG-SH$2uq6V>{)k{%*w^G!=28#Ok2)tgTsMbQN;AS6u zz4Qr+x^R0E)ez$w!t_#z<9Lc>^-RH`_z2k$@_vV(a@0@pUDeqX;akOmSi~f&p zxc*$vx`R#Z>pjPezjZu+=1Idq^D$%R-XFAm{PfEk7hLEmdSK+{{+(6li@$g2lc!$Z z=)Tf(+r=GKTL*t!w*JNyZ!Y|yKc2jL-zzig=GcADe0p`>%BxjR?Amli%#~Mo;ff)jXys0)x$#t)E_P{^X!SO+^fG(+qksZvuDYw_5TG3LO@~w literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/components/loading_spinner.gif b/runelite-client/src/main/resources/net/runelite/client/ui/components/loading_spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..81e7c2845d79c08684d7c55f06053915b6e5806a GIT binary patch literal 32397 zcmeHwcUY8n);2+;mtklFj>rr{6_73{pbSF?0YOE@=ui|C8(0A8GxT0m6qGI~7P^3q zCW4}3*{o(wR@6jvO;(MYHCgxF@BE(UnPC{Ph5A=%T^ar7x||M`uggtubiBmyk`3H_VW6Cd3$+(zPvy3CG_8VuJ9(x$J^7#+XFA( z6&_H2E8L-0u5kDFfpQDLYL$;0QNC`gh;m)!OVny#SFBvt_@WB*bB0>Wt92_`>sPXZ zRyqawJ8kfH`Wh;LxiNsbDZr7v$}xD=a;Olf)yqOxFWbDvAw1ANBG7K@TDxs)mqxBz zx_#Y}9qVm(uD9KV%4TwblNwtztJ?9@u1&$Tmw3HsypYN)I*8 z-n=k(i(!7aL1Bbm$yVLcZ90dx=^Tz^9NkW@*g>n@sa3s8vo30WWAwbHy&9+Ys-N9Q zIR~|$(!8J25`(HUPPIE;s)`zG%NrUR8XFr=o;-Q#)TySXrqicSpE+~p z?Af#D&Yf#+ZfDz7Yinz7Z|~^n=eZ`5LqpfDT^k-A9vKfKK%RN|NgK4`mg`|=RdUn$gFS<@ON_bbv8FKo%0pof%co2)Ep7Ih!Fw@ zf{BP^w}`VO)vdXzk0YsM7&P2mJ-|`bRDIOyES9Th${H=BwJ7T3`Pl61rWeWS*h$Ce zQ|Oyr2NtH^&8)HreJra<5B%OYCX8HfBT?RL))vjyG?R!8U`r0=7H}<&-)9A;k?WUB z-MoMDDVcq*L9%wms=x-%F*DhdTG@_WX90(@uFw;{Qf{^8Y3ShrFn$=$tuo zBqb$vb#)C54J|D#oSj)-UY?)FJO48Qq5l8jm)8n!Q~-QmC<5|;`bppe_W9+%!VQ&= zaN!>d0Y0u!tDuNa09OqD@Co38;J?y&-AZS`K7zkL5B?bT5&TyH{1NsM{8ujvTkU{j ze`_EDK0-ZKJJ%7|k0OA-7z4g_%touYO%@4k^Q2(2)R0AKp(Yt&##x(mfWPOdejm)1y|CA?+P`;H!W%F*;*7rdMRwCYCyEhZ*5 zckWy|oo;V$@9OI6>FGHWuM7X~b0`*K>6u^R@SXY{I2@}Lo<1uGObZ~J;B!RjJSg*6 zT>!X#E^GV{aB=De(#C5lbu)45-WcEr?2QrlasYA2DuTMVtg(ke>>aoiK#T}{?UKlK zO90Inpm8W84vz|29K8WSIcB3}+(ygzP38oFhaxDahZ$#t8D(u=n6t%@hi06~buk*( zMo`|ppmsOVIAU_Z^oeLx4bk%(_sl=JXMWRO>gj#dmROCBILi5WN>_qx4t1Z{PHQ%?TV0ClRhI1uhXAW*ZUADLBXyU13nI{p4SIlxapTi}) zM2v+1ogvp0pcwA7DMyv0?p>o_C`HbU(En45NWW--o{G8$EntXQp*?5)<7TaA9vr?2ZJx!cScS;_U$_5Qu&b+Hq?{p~rmNWyhV)p5%n#{7fSukmxZ)c#5UEG z%(64V&KE=$34xijPE++_)DKLG2ixRyAg!pdMrND-nQqg%43=f#k6JV?OPs7GcSgi8 z`m0+-DGNh`CKR2wFc)U1cGyztZm3pDzlgC7pCdJ3%uZ#L`kAwpS((2wUZslX<#B!M z>2+J9XcgJw1(EDwF<+XQMD1{FtrJbjexph9Zo^$}ES<2I=C91}xlreA_?cA{-f%l@ zVZ7m8_1{QY_w?gc71ZfPk8j?p0(Lew(<1P!{}DXnief`S5xL{d~#B$LUis;bbMTU%Q@Iy$==#n*c;H! zo4~R0d$Q-o=$hYWyR7y_g%8_;^Y%Ifa@1|(4L8DefD<-pGx13q0ecMs_7-f+Mw9kh zV$$BW4u)-vrSV}Kqi8f{e{H=VYqjIq<}hGGs|~;geg<;JXY8V_dH`vBx<0m(1|2pK zGQ#sd7^hX+VpKX~m3j^+UPgG99ZHtDmMjgZ9!ZhDo+^c58{mzX^e6{P<_1^x23P(% zmo$>5G@Pb3luo^xsXdUrus7H8QvR~;BJYmU4b4ZQnkus!>yFLhnZR?Wh@UW?gV5|e z_iCAY2t%G)Dw$U9zQb4YH`?x-wM)FktzOVrE(1o%qg9!;+^QQ|esTJv!ESfTIn2(W zJKbaQWt?TXrXS2*4Ei-)vKj4B6QoLR&}f-rapB2(9N&7AT%|iz#jVn{Vsys6DizzE zUAnogdbwA1=i~)Mwsuq>E@M8Cd=vFNdvIxjZ{)at%Poy8@7F8Go#v($ozvL1_0ec| z+m@`az9OKk^WT87l9JNAdGqx2^vuo8mn~ZcAm*d+`Cv`IznU{lH7PbaW%{ZDPkZxv=jnDW5y%Z*B zL7LvPVe!7N@xj>=2Iu5pGY-~m14n0LQ!_pB+CsZ%U(^E9ZHeCmLfBfO4PMfIjEsj`Jq(ORStQOqddS->*rE? z(`c773@&C_bm!Q2=DW8Q1)e>)?bOkfhRWiG**;r9E0eq=;yDA9M~myl)ZIfp;(w!? zja8GkGdc`tBZY%jxi3wn1{nnm8@ZMbX8!d2;*^aqOJ7&B(wRnFQl8~Rh=)?8%umU- z6CsjLi-L0*rDhDbe#12BpWHK@b14g?sy&?D{`9inX1%=b$*x`37dTqVt%$81GU_*o z>n|nU3$0Q#(kaVqX&SrXwP1IAOpCwsllcxh&9A=llE@p>O=x-*5gB@#m9lsl49-36 zgUJM|Df(17r?I-U zvF`Y+`VHzg2-{A=i2hnr^;y3T_lU=4CRa)unr+f9!#*iLsiHyag4Z^*XI!6pTe%i7 z52e)nk`(r9?k>O3RHjk7G9a4i?voxfNUccbj2|}lAiw=k^!j?WQv!LNqV@@c{zaiR z^or}gE1Ws*?WPr?;bTWQ{*M}~kM5FQP#;ny8&va+lzNZM6_F*sINefMwEwKzXvr#> z1s!|tyK!s&{BtSE^N<^(F&3MgJ!N^q_LuN8wAaeY%0R_*8V%Uk*x1<8($a3}Ql_J$ zi;Igp^wu68ULcfxnO4T9?#}^w`rkhVac`dSg-zSogpGP`V#3DTZZu3!^|<&fEu@d( zAjW5C(8SmQ$j-uzEYP!YNgD()!qGye7Osh*85$SFz|jIb3ydt#!46;|SV}M`U_^Ue z6roqTRp;+oGQa}XjO42-^tlg1f*v2t$PIowy>3%9_&545Wn$m>IBYBk- zhpMWos;jGOYHDh0YwPOj>g(%IoH#M7gH7sS?bG>rEgGHc#V9VDJR%>`cl%V4!ztAU zeUgPNi?k<358Jr%+SYO>A4~bC-Yia8|3tFAxiGtOxwgVLk4H@9)w1Tc#kd!bF%`9> zYTv!^*Bd7sCQ^l6O-!EZX)xwA%eVTK<>@1_Ll3aewHC z>G1o=Z$5)c0H$E^d+KRF5N0*swI;l4o?14gT`qdaMWAfPKr`zxShF~Xu6SAwN(%J8Ek5N zkUp|q`;8RRh-r$Hg>J^*yBcqtw*PU=lm=ud=}92#>{Uy@>961gu~ zxi3YnH+9|>&VtKa#-;Ry7cwona+V?Is$@fR+0Jtn31@2Z&eR`1bF%t>ttl-zg9f%< zQ+2fX2T^seP!njhi%7LHJ54WjZ;B`yv?_dMWA=>Ya{_2t#5|gP3mWZLL5=q^LzqTc zln&cWJ%8Ow>7TT88pTNi#v#S}b&hn}K~~l~hTW4v_f_2W!*1Sg2WeCzi7Yp^{?&r2 zT=A0J-$mX&K4$v zqb$mdZWgAE38YZ42nyDFqp=#BtazSlkU8PC+)aDbLFk5ox@Es=>wXmhNRtty;}N7UB`97@AYa62`(mOB*w7b} zsTWeTdQx?|xJI4nmhD-~Tk?FsvO05U$LXW-O;x!~wTGG-s{WUUOeD{XY!Pm=H)^WB zdlDwD;TgU;7l2e)qaI~6a>UV~h-IDsI>=y8*zNiya@`+B0=Hc6HH+E&U@gmqcGxM~ zMdN<-iJOyrFpjA)C#ssk`NWcQ@OU%VnQe-^p#Zw|eo>UN)5}9sD z5z;j_6cckXmKDK;FTA}bI&3U6h8kILQ$Bt{@A24*wB7SD~~fiVCt zjLDE0!_8*Mi~({FZDRlp<7qLlnenz8#YOh2!^n&U#t?*|1qTRs#gU=&M#>n_aH8B` z5+;iwsapn=v1>>llf9NIe=U_X#8DdJstu-V4rJ=}=2~4Ybm}Qx)p=w~`>}nkl}XLj zS!e4?PMSHPD2WMQ^!XB@;}|>K=0|WSL}qBm2UFkzZ#a zSMqoy=ErNj`rAzE3@n}5#mhLo)JTp2V0qhd>a*euc8P&KS$C}kdAw*@(i4UeSIYk& z*ZUO9*{0(Z(^N%nh|;txbH4p*`Jdg28LYopdz)Cc7?=(f$|U||9>m<@A!}ro9jQn+ z^DdWhy-V%hr1STI1=eYGY^ruTD|M*@62aa*=XKk9)7V)a6Nr%09`7!&;AbIdz6M}ksekRaiQNO(#YFAqk@TqqZc zMML@V5j^%O0n6l1_f*YDp_G5nKY0>?oUIB&<7`F3X^4PU7d6(+MnEScpdf3+8>l{TrS z*tSeO9WdtHm%U@bkgqeBD|&z-T5sEmIz@&vjqH$gnYt(Xh2QP8tu`9(qB6$G2204A zYb|tXMfL~2XDIJ^vGO3-6GQY%riq*U*S&qahN(+8%3YW2f127)i?lJ3GABuFRJVC$ z&LZn&%Nj*r^)MRvaj?+B$?`-3g}bxRB7S%C!3T#i5iFXY?wm0U1|G9JG{}GaYZ94% z_@~eF_EdfbB^yo6Se%52c?uIXo+w#?$Gwr$JUJjw33Q`W7-ld#@dDjArco1($2q~6 z!WA&goC*-(MqI0t`7*mkrMmpFwBKn0m zc^3+D0+dkzY&WF80L}A27Q^D4I6tf5%{4YGLuaiBoi!52FntV0=F5qSmlMgClawzb z8GGI(4*g=fQFqqj&RplV0>9=GcGKa#C(F|sj_21`ADI=wrU$wu&rJ)1aeNRz4>Aw2 z)*5*op})DMDHBZ2YT1=pFld~7U$R@9kzZ>rcX)Ip&}F#SV4G3hZ%)qRWV-`B)ICmF z1e%!{Y$ZcGG9CDtR=jloBgO}(th)!fKDF!WukVr}XU$!I%Qky;RiTw)`FlzkU1A+$ zdre(caq2at3kp{oTeMiYH+8lw_`U3JPNR>M4Zf8YGwN>r;YOK^I4=VxBxFN~69OQb z^XD_PwG9jmOpJ}eezLN(grRsT48_36%a*|`jFjt-eEk7No`Q2>4D%5?iB&8N!t5~t#>^&+f&sCJg?RyN%xvQM$k1Tp8EhVCA%=~T*qECv5ar}S7lpAAdhtS;PP~Af z1!E(~XvTRN%Yy-oc~X}(G}!UjE=CHzixGx)G4i&cMGO$Kp{Xu|ad7 z=K|Qp05=L?3^6d|N%3F|V=^!@CVQy>DW8u+!LaTFkPU;;7){4L393Dbl%6DNPYS)8 zYuJ@;(UIlQmgC-Bu=-5NmXl=%8jf;L9Luk(Dw`FuXI#nv{cD=h?53$|w6bnc*~#C- zGJ@W+;y8JO>|O&uw*rz3i*?S` z1(u2#W0aV2=Pc{2m*%oH;_lKq249Q0?(jZfDIh1d zu%@7y_X0yT-hcC!wQykbqal1A#_VLlWG4~#;^P^IG>V1@IL8>C`6LWmRRo^##v0eN zu+54|LdI7t@b7$bM)nj>;led+e##SPW(?A3Y{u8Cu)GLzl(Db~>{fxuMJQYl{enOz z>{fx!1>{@-*xsoHf)@ZAQ8Y?tW34t!&Zy5;L#U2{$yv26R;4{wr6W$Y^MK0vcvXmL zcgD}_NYv^`)@e^QX-%_f$#6WE?Rh$H?a88u#)JD#98IeSJXg-*dFCZ6$sQ4R;SSqf zQ`IthSAvi1%rn17@p;7&E!%BvzYYU{1mTm3f=BygfO-Cy$-OQ zNWV+3l)l5VEK8Fgpn2_}i?!-{F*Wy0G`l~@AlK_baKs)3M+}zBAMGCSbuC})DKqru zs9CkVg_E0zx@V0j!n283@K9z3P1A1p^}lvWtC5!+f22%f*5sF49v>@=X*NguQ#pqy zq?si!pl66~0+2zWp3c93%FllBfcb_06lUdVp%C7xB;HN0a0lyHz&++em{^-c`%zP} zAkbE0xlNww#G_{->9yXU1tmbzXjPJ5gc(JwPT>GY-}$s13Yjnz@8n0f(eMl_lsH4< zV>320Tk=4S?@Zz|GogQh8C9~4FryAdBEklA1GKU2Db#L5mks(gU-s%vfC(99V@Qg~ zg7O8KFf1=3-=7R&5lD)l{`vw(yNhewo@swJ$ETq%qV7;aZFyeJu|u;y^CxyDC9h1E zz??Nzspil6OPD?;`hH*#9hccCozU$Y4scEwd)8bsAV;^5OX!V=O7Y51@%Sdl?Pt87H?u)K>aY(KDCeQWLdx$`QLA@6xhN>yGLN z_y65DYtB**%7H~pW;O??Kctn;;ljxeK;nI@d+GqB>eR%fsG7gJi`bLve-5P z)A;@cu?-Cg5**X`@&#W915F!5FyLy;W9Dr`BUn#vL_*^YwiM8EdyERce1ZGQAZUZN zj1}n$8km(XC8=Q>k+d$3VLR8dIm59jXH{L{uIfW+mE{L3D=TNSBcI-b28y00YeUN| zOMWI=>ga1?w3c2Z511BD=r%~Ez; zmQ48GP75!let@yLkYPaW_;9b$&jniU)cWg%BL$pr2b0mF&E71|ZY|^f z8q;fsvu%TAN6nuz=Q-_B|8Y3V`qX^49SoC^VY)<)OrflMZuR-)`J!sAk_T-_7N4eQ zIBS4++*(>%Gotmgzx+5(Kk=6-5awG~{2_XJ(=9kwPZPfcZXNJ~Uxfa}8>Oe+G?+A$ z5vZ~4X8bz_YHY8HN8LQ$CKwy&8pkq9YXg>f%g+2TCy3k7cY{%t$XmGpEi5z47*?Ah z%$XOCE*qfK2yxti(8VA!0_}DKj%6$yMu=Z=m;Y<3Rff*P@zg$!ZU|^#|F-<>I&Fg94pKkI*EZ+IfUJv*nEJOy%_vv&60@V-!8cU%#M!dWu$5 z1iFTFHVo3Xwzi)hm1l(eCxHBsKTXH?WIRj=Tnuz$I?zykP?a0wdALh_;UTn&&)2Iy-m~K1+l144YKh zn^AHEZ8~FfHF#DqPQ!8}9@7TT8QZ`Bt62qkQ%K521!5Qs&+T!Pwzzq%2NpEPGtMR$ zH6>d&q_S$#S663;Ru=57IG9yYR$Nh0aqQT!SxkS)sS;4dY=x(2@Tu&gxK7H;{A^%~ zHj|$(zwtx-xX464Yhldjhr(Ytd0G~7d5tNe#n+bWhTBv;bh4-}vWY%T4e)pK^+^i_ ziY8}N7c#6BHQk+b*`C&>ioZK@-Sqn#{1jH4=^?#zwAf4ec9`ih>}ZoKAL|^hoxNUF zRy?j~z%j3?TY4>}rH@NeT9W(7?Zf!5afc=JYTfx%jUtXb>)Ce^a_E1jT_ zakUcHz~LCt6h9g;45}D^ITAlb0tAgMQt?%8fj}p=5D7?53qvzBTp)qU#9S*x*5Qb( z;SLFm(Qtl*w{}5=dm|`7vY!I#7m~FPD4#z7GME}%D`<|L-yElP_5kC|0fSQsrVU9; z>QY>*($^i&jyP5rS6-S`ez>T-ynGhVUw(Q>vVFQLhKurNB6#qx%bpd%Tz9a z<|vKln}@m3A4N|T`XZIPV%@57Qn~oRFgtus_CZaFheqb1&eeI0a4B=0@zxy!jjrPJ zi`w+_Sc-JF;V>trpH|kK8%Bn6RxS)BZ!ke|ZHob3fJ9*`F{`Vqd+@BNPpe-%#QmXv z&J53~D-(p5JSk(NK<1=5#q*>vqUD_inqp32IK^k=sfLOjQm`omy`X?}q0k0%hzS%ID%2RoMR@5i%%#HHX|Sk> zLt!`vgXjghFGRz<8wEsM6N_K>B|;`dVJHQ59_6obNY}XJ;WU+@boHwl^9M8O{aFUR z*^4gaSoh>QoX>LwgZW(1hNhCO4ToZDj%3dg^B3L_gJyeyFfrR`s@6F^?NhK>rulAy zR^(YG;r^HrYub>@qJ>s8zwZ~#Ii@ei4#+CwUhUOfr5O+bK$bP$5T|;v=6U`Y*FBON z^frwqqoGCqwpmn(oo%=(lTrHgTES`p$TXRjxhD(RlA^8(<>HrQ-&&Z{BsHu@+|(b4 zjdG0kzcl&KukWp3Z$HF%LRTECk=Q`l8>N2%tu=e9aLTy5RH|92x;rDuHc=L0kak zA?Qc}#>prM1|$q|Y*4h(8L;h;#YShqa4Lor7;Hd7cMYZ#iep2T6V@x@z-m_NiHEf* zMG(DuVV@$I+@GQZX|FzxdM{V=N*euAy8eYs)9%d0o!MYGyEf3(l z)+{;y0dhVsVlRwoJ5AMg_5i2I+QshuYucS0ih|F#c5h6d=d!fJPCbwe3CTHTD7Sr6 zR@bwu%NGRNZHjPWv2QH0m)mhCTCcHRXNkPSZ(XM8dE!#LLtBc+oDUkRD_(!|bc;K) z!B_Fg1Lodp8F7i0vIxmvHy=GY-O^{-qE&XL>maSOL*XxPzXq zehGEcd>Na!Ey4Gzurr@inrn2@PtbvextKp13o2f`%V5M^Pkf0PDc0y97?$E(Ylo#c zi9Q+~7QxmYLFfWFLns@PBLHf+GlJn74uYYZVEB1I?BEEt${cP0R|=qh5qV-^&Q@^@F)BS`-t111 zS^w((21>f$Z9C>LmHDsN1$3E>l!O)-+XoHeJ528eS0jFArwaL1D%1EGG9=5FFBi&W z3vu#{$eszopZJ|HxcQtc0B8ZNOTeST&v)Vm75XmhQQ`O|f@0HxB`#n?3l1y|h2tB1DXc8uHUR;2OxK1MoWFX3of1NdHY`En zdlx{_cqR;TortCPK&}&HFbWpYR_TlbWGnZ?D_=-ZzMQCh5qznnc^8s3ds4M~IC`)p zb)IY1k-oSs!=X9TR$B&lGV){!leNkkgFh65GcWo-kl4%J1 zygTQxw8xnKNfN>|`_RSrZ-Z}C=*q1!I+@7=ehx77VwadzOB<}AYdk7$SmBe#G}MR` zO=pXiOYPp2Qf!(cKi@;)`uKo^`>ICC`js2PpWo;+45NI_9(23uXqx;!*DEG$tV}xW zglr`Jgy`X=(Q4UjW!GU%T9|A?{DiCDT+`g6|JA3xJNtX%+|Gb-QU52sE!Yvy-`UHF zPziY|p!pN@jF6r2m!H7w$NwaZW{j{26`L=1&5UfU^PX~&7X?1~HObbPqU{D=6(VUA z)W&P__%`yOrzl|P5I5g<=3^S4tRdtHZ8u>{Vd6C(*4WK$G+blgCN6ISzVTTbyE=lh zB+z-|yO8+pPKb%YE)@jCAoGdkM^I=R&VzyDjK{SRrs3u`N`$Gk$Ev~E?ehoJAlM1_ zI=d37T}cbhCo?)z^g2=&wxyc3a;%%v?9Qe;ozC<+m9wfbZ_|mw?X{%`s>-q}%V!;{ zFFM36*(RbR3}_(fQkqk$f`tPEhUhoaYErJF)S}+>T$bj>{Pu~v%o|i4IakWbiIBlw zO@9Ug^mwMW2VLWSR0aUrejSx1>O$|QuBp?fvMtyZ_6J;+%$HpCvVZAD`4wgZucftJ zBIQ$m8*p9NzmVf~e8j*viy{^DbJr^UbU*5XI1^Rre~lH;r8ZHl{>^cHQd$v2NeB>) zZW23p$O5Thn|L@D62m~8o)N6mfBA8Ye&Js}3S}W23%wLnviXM8BuVo|XcAuU2!QBzH3v`2-V)5bGl{A>C#$aW@{Bk=?` z@2)lqaRWWW9eto@7^0!^Mq3!z4PN}B_Nl#amISW;X~6k>9Linss&Ga7auRGXt9QfI zktBM1vR-?#VOxqxOR7aP$L3txvNIVjr?XbT1(L@6;1k6=>JG-&mT_y!^Qw;>o(+e6 zIvh4l3bW*h&i0)2(L z(~32|$pv&3x!`W6jOYFHtYi#->nF(`)s~9h)R#rd(RSsKhsFn78BEiNDtf8MDw^gA z@iSHI%(Noy#S&LOjB9tktTI=)@21|!8l7Xj|D~bwQ@8v=d*+z(OZpq18VR;?TC528 z-UbIc^zXi1KH`}*=bysO_KY_?DQaredGoXuEP$;HxFj@rssLM+M5hWI93Zw0tCDc7 zz?sE@3*7&$BL!ddDKNulfB<>(_N%6z(Z{7SKE5X#@2Mm8^q?3-Krvwq_1;1uFt`hb zgIn;h5O!%B*;G4-bNT4THjZ+*w2f~|0#yrM+QvmP>}Wo^n2)bm0ls+~8o2z8Zfs-t zMt8MOVMn#k#b~s~X+T=LCjssWX`D~g>`bC{Br#f(44RV{olCJen`(20>u@^Fxhd28 zWH#dIhN5jJ4#w0S$*8L+sI5FQyN@yRK1TQS8=j76fS%(_GFRlV;)lhgrL{wIs{qr% zBtuqsXtkrxEpmn>GqBXaG}BdoJInCV9nR)HqnK9{0fBEGlTBr>osP{Sxv3UNb}UZ~ z)gP~LNGOKGLhq{l8>RoDW##59H(*qtb=6iRIPhs5$=JUMLgAs5(TZ4~0hvfIRAZLGUSmv^T~+vu_% zfA<15ow4IVJf23k3()xj;+YqG5c4)9iI68~WVkd2Ew{iqURbljpLv1K@#14NTE)PQ zY=e!3?@8h^8J6HidsSU=%H3#^28~P^f8I@RveH0`(qJmw?NlA$YV@aR_N8n0W+D&k zO0M(N8Z5mFQ&<1#+s@O z$9|@qkrgoXgMo~>^8@z;>bE%u%h0UcY-o4H5iwpR~Ass#sJSP0eDF;y1~~O4Z69rJ_1PC3*#}+@M=5dvw8IsgB$dg8|>`HSUq} z!7pmJymB;1d2l?!{Iw)SCSc-$w`6+J9lIs42~EFqnV*WN1HY22b|ppqO6vT}9IeY-9XJSfG1Kfqw#|jSWjzIMUBxRq zOV_m@3T`di+I%$nY(@O((pv^)Edv4Mi$T3`Ge!r3S=BjMo zDx1`9`W9=htv+HYb*oQj$NgskWv-)rnyV-~4DONH za(d2Wk8ySVarLG9Vi=F2PQ2)A@KgI{=Wrw zo6WPJ`JJ}VB#i-jGSrCx&12}c=08owqW&i*H literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/components/loading_spinner_darker.gif b/runelite-client/src/main/resources/net/runelite/client/ui/components/loading_spinner_darker.gif new file mode 100644 index 0000000000000000000000000000000000000000..e44fcdab65795bc58e9104874263b02cc0c1e8aa GIT binary patch literal 23668 zcmeI4d03NY*8dR#Bq0f5Par@D0Z~wcB7!ShG%9Gs4J|6077;Xn>{~(tgjGNkR8%$r z5fQ-!a4D;z(3YxgvEy`bX=|71pzX|bo0;FapC<_cZ0G&G+su1iaGeW-{>9_FncyChl!fwFdJ$vUUOzU&asDbf|_eTZ?64( zC`Y>mSn(X~cuscCSS_5#T?93s>w?wd`P{_|P`N6~b_vhcjc4o5vt8T}hO!9svDnSG*uysuUTq$_#(b}Uxo<5qd>u1lJtNYWanR39w9zb9Xd1W4G;y;@ z@)nbnttP2kjnlRnr*AhFZ#R|%7-#M`V793ojb4VbK_Romf zl@b?`n3$N9l$4yDoRX4~nwpxHmX@BLo{^Cu7K6D?4`V*zx1X%gf6vDk@H# zIC1ji$y29JRaRD>K7IPknKNh4o~^2?s;;iCsi~>0t*xu8J9q9}eSLjHLqlU@+qOP4OSwzgire7UWyt-Zbd%9SfuuU@@&?b`M0*E>2oIy*bN zy1H)MxN-C5&0Du_-M)SMop;{p?(XjC>FMq5?d$97@9!TN7`Sui&fU9r2L}i5-Me@H z{{5k$q2b}-ci(;Yz4zXG|NZwzMn*pP;DZMb9z1;b@X@13AAb1ZM<0Fk@y8#Jj*dQl z{P>ekKKb46e)r_bli&aT_n&_H=^y^^hq1A-r%#_gd-m-4^XD&Ky!h<1&p!YB^Dn;m z;^oViUw--JS6_Yg_19m2^UXJZ{No?L{r1~G{pnBR^QW_x<xwf^4Guq^@ksR`0>XdfBNaCpMU=OmtTJQ+u#27_rL%BAOHA=%|G-u@V9LC_7w6L z&gW^(0v>FBB#;OiWDO?-2f{>yctgX4$P%>6bghv zv$O{mMeriF@N9JktRw2f%6Pr-|0sNKT&Qv9Ublghv3T&Q*Hh}^hz?fe)6g}#*;}eu z1;PH+x|T~LxCBYaiyj9LZ;o&8hdR9tn;N$LzHubiPby@-$Btcn-_vSSMVYXIFwCFL z*ZG!M)16h4Pl+m<^NdrFZ~4~ArB4jA!z!5My2`i;pX|`VdFeuyyqcK(Zk2bwm7g

(Lv{VuVnphc}tV`I{zep5Pv! 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 Np1{l#_{}Hq{{S@k?;ZdE literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/components/search.png b/runelite-client/src/main/resources/net/runelite/client/ui/components/search.png new file mode 100644 index 0000000000000000000000000000000000000000..e68566fe4ddb29a138354ceeb4600d76947caf6a GIT binary patch literal 16259 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 eyDwcNh&9jt=JkVbj8@VE?+3J!<> literal 0 HcmV?d00001