From 327c7b9c3b1fb42c9fc047cdd367f7c760524056 Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Sun, 22 Apr 2018 03:28:45 +0100 Subject: [PATCH 01/15] Restyled the client frame - Created new substance theme "Obsidian" - Created new ColorScheme file to hold all globally used colors - Darkened the client frame using the substance color scheme file - Changed substance's colorization factor to 1 - Tweaked the coloring on the dev plugin panel - Set the UI default foreground color on Buttons and MenuItems to White. --- .../plugins/devtools/DevToolsPanel.java | 4 + .../java/net/runelite/client/ui/ClientUI.java | 8 +- .../net/runelite/client/ui/ColorScheme.java | 60 +++++ .../net/runelite/client/ui/PluginPanel.java | 2 + .../runelite/client/ui/skin/ObsidianSkin.java | 220 ++++++++++++++++++ .../ui/skin/SubstanceRuneLiteLookAndFeel.java | 36 +++ .../net/runelite/client/util/SwingUtil.java | 6 +- .../client/ui/skin/RuneLite.colorschemes | 166 +++++++++++++ 8 files changed, 499 insertions(+), 3 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/skin/ObsidianSkin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/skin/SubstanceRuneLiteLookAndFeel.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/skin/RuneLite.colorschemes diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java index bbd126e943..e7aba556ea 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java @@ -32,6 +32,7 @@ import javax.swing.JButton; import javax.swing.JPanel; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; +import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; @Slf4j @@ -52,6 +53,8 @@ public class DevToolsPanel extends PluginPanel this.plugin = plugin; this.widgetInspector = widgetInspector; + setBackground(ColorScheme.DARK_GRAY_COLOR); + varTracker = new VarTracker(client); add(createOptionsPanel()); } @@ -59,6 +62,7 @@ public class DevToolsPanel extends PluginPanel private JPanel createOptionsPanel() { final JPanel container = new JPanel(); + container.setBackground(ColorScheme.DARK_GRAY_COLOR); container.setLayout(new GridLayout(0, 2, 3, 3)); final JButton renderPlayersBtn = new JButton("Players"); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java index 16c55e4bfc..e67f79adc0 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java @@ -67,11 +67,12 @@ import net.runelite.client.events.PluginToolbarButtonAdded; import net.runelite.client.events.PluginToolbarButtonRemoved; import net.runelite.client.events.TitleToolbarButtonAdded; import net.runelite.client.events.TitleToolbarButtonRemoved; +import net.runelite.client.ui.skin.SubstanceRuneLiteLookAndFeel; import net.runelite.client.input.KeyManager; import net.runelite.client.util.OSType; import net.runelite.client.util.OSXUtil; import net.runelite.client.util.SwingUtil; -import org.pushingpixels.substance.api.skin.SubstanceGraphiteLookAndFeel; +import org.pushingpixels.substance.internal.SubstanceSynapse; import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities; import org.pushingpixels.substance.internal.utils.SubstanceTitlePaneUtilities; @@ -327,7 +328,7 @@ public class ClientUI SwingUtil.setupDefaults(); // Use substance look and feel - SwingUtil.setTheme(new SubstanceGraphiteLookAndFeel()); + SwingUtil.setTheme(new SubstanceRuneLiteLookAndFeel()); // Use custom UI font SwingUtil.setFont(FontManager.getRunescapeFont()); @@ -362,6 +363,8 @@ public class ClientUI navContainer.setLayout(new BorderLayout(0, 0)); navContainer.setMinimumSize(new Dimension(0, 0)); navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE)); + // To reduce substance's colorization (tinting) + navContainer.putClientProperty(SubstanceSynapse.COLORIZATION_FACTOR, 1.0); container.add(navContainer); pluginToolbar = new ClientPluginToolbar(); @@ -565,6 +568,7 @@ public class ClientUI /** * Get offset of game canvas in game window + * * @return game canvas offset */ public Point getCanvasOffset() 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 new file mode 100644 index 0000000000..1f5f9d7878 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java @@ -0,0 +1,60 @@ +/* + * 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; + +import java.awt.Color; + +/** + * This class serves to hold commonly used UI colors. + */ +public class ColorScheme +{ + /* The orange color used for the branding's accents */ + public static final Color BRAND_ORANGE = new Color(220, 138, 0); + + public static final Color DARKER_GRAY_COLOR = new Color(30, 30, 30); + public static final Color DARK_GRAY_COLOR = new Color(40, 40, 40); + public static final Color MEDIUM_GRAY_COLOR = new Color(77, 77, 77); + public static final Color LIGHT_GRAY_COLOR = new Color(165, 165, 165); + public static final Color DARK_GRAY_HOVER_COLOR = new Color(35, 35, 35); + + /* The color for the green progress bar (used in ge offers, farming tracker, etc)*/ + public static final Color PROGRESS_COMPLETE_COLOR = new Color(55, 240, 70); + + /* The color for the red progress bar (used in ge offers, farming tracker, etc)*/ + public static final Color PROGRESS_ERROR_COLOR = new Color(230, 30, 30); + + /* The color for the orange progress bar (used in ge offers, farming tracker, etc)*/ + public static final Color PROGRESS_INPROGRESS_COLOR = new Color(230, 150, 30); + + /* The color for the price indicator in the ge search results */ + public static final Color GRAND_EXCHANGE_PRICE = new Color(110, 225, 110); + + /* The color for the high alch indicator in the ge search results */ + public static final Color GRAND_EXCHANGE_ALCH = new Color(240, 207, 123); + + /* The background color of the scrollbar's track */ + public static final Color SCROLL_TRACK_COLOR = new Color(25, 25, 25); +} \ No newline at end of file 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 36dd220fcc..ca93c74fe0 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 @@ -60,10 +60,12 @@ public abstract class PluginPanel extends JPanel { setBorder(BORDER_PADDING); setLayout(new GridLayout(0, 1, 0, 3)); + setBackground(ColorScheme.DARK_GRAY_COLOR); final JPanel northPanel = new JPanel(); northPanel.setLayout(new BorderLayout()); northPanel.add(this, BorderLayout.NORTH); + northPanel.setBackground(ColorScheme.DARK_GRAY_COLOR); scrollPane = new JScrollPane(northPanel); scrollPane.getVerticalScrollBar().setUnitIncrement(16); //Otherwise scrollspeed is really slow diff --git a/runelite-client/src/main/java/net/runelite/client/ui/skin/ObsidianSkin.java b/runelite-client/src/main/java/net/runelite/client/ui/skin/ObsidianSkin.java new file mode 100644 index 0000000000..6cd6e140d4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/skin/ObsidianSkin.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * 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.skin; + +import javax.swing.AbstractButton; +import org.pushingpixels.substance.api.ComponentState; +import org.pushingpixels.substance.api.SubstanceColorSchemeBundle; +import org.pushingpixels.substance.api.SubstanceSkin; +import org.pushingpixels.substance.api.SubstanceSlices.ColorSchemeAssociationKind; +import org.pushingpixels.substance.api.SubstanceSlices.DecorationAreaType; +import org.pushingpixels.substance.api.colorscheme.ColorSchemeSingleColorQuery; +import org.pushingpixels.substance.api.colorscheme.SubstanceColorScheme; +import org.pushingpixels.substance.api.painter.border.ClassicBorderPainter; +import org.pushingpixels.substance.api.painter.border.CompositeBorderPainter; +import org.pushingpixels.substance.api.painter.border.DelegateBorderPainter; +import org.pushingpixels.substance.api.painter.decoration.MatteDecorationPainter; +import org.pushingpixels.substance.api.painter.fill.FractionBasedFillPainter; +import org.pushingpixels.substance.api.painter.highlight.ClassicHighlightPainter; +import org.pushingpixels.substance.api.painter.overlay.BottomLineOverlayPainter; +import org.pushingpixels.substance.api.painter.overlay.BottomShadowOverlayPainter; +import org.pushingpixels.substance.api.painter.overlay.TopBezelOverlayPainter; +import org.pushingpixels.substance.api.painter.overlay.TopLineOverlayPainter; +import org.pushingpixels.substance.api.shaper.ClassicButtonShaper; +import org.pushingpixels.substance.internal.utils.SubstanceColorUtilities; + +public class ObsidianSkin extends SubstanceSkin +{ + /** + * Display name for this skin. + */ + private static final String NAME = "RuneLite"; + + /** + * Creates a new RuneLite skin. + */ + ObsidianSkin() + { + final SubstanceSkin.ColorSchemes schemes = SubstanceSkin + .getColorSchemes(getClass().getResource(NAME + ".colorschemes")); + final SubstanceColorScheme activeScheme = schemes.get("RuneLite Active"); + final SubstanceColorScheme enabledScheme = schemes.get("RuneLite Enabled"); + + final SubstanceColorSchemeBundle defaultSchemeBundle = new SubstanceColorSchemeBundle( + activeScheme, enabledScheme, enabledScheme); + defaultSchemeBundle.registerColorScheme(enabledScheme, 0.6f, + ComponentState.DISABLED_UNSELECTED); + defaultSchemeBundle.registerColorScheme(activeScheme, 0.6f, + ComponentState.DISABLED_SELECTED); + + // borders + final SubstanceColorScheme borderDisabledSelectedScheme = schemes + .get("RuneLite Selected Disabled Border"); + final SubstanceColorScheme borderScheme = schemes.get("RuneLite Border"); + defaultSchemeBundle.registerColorScheme(borderDisabledSelectedScheme, + ColorSchemeAssociationKind.BORDER, ComponentState.DISABLED_SELECTED); + defaultSchemeBundle.registerColorScheme(borderScheme, ColorSchemeAssociationKind.BORDER); + + // marks + final SubstanceColorScheme markActiveScheme = schemes.get("RuneLite Mark Active"); + defaultSchemeBundle.registerColorScheme(markActiveScheme, ColorSchemeAssociationKind.MARK, + ComponentState.getActiveStates()); + defaultSchemeBundle.registerColorScheme(markActiveScheme, 0.6f, + ColorSchemeAssociationKind.MARK, ComponentState.DISABLED_SELECTED, + ComponentState.DISABLED_UNSELECTED); + + // separators + final SubstanceColorScheme separatorScheme = schemes.get("RuneLite Separator"); + defaultSchemeBundle.registerColorScheme(separatorScheme, + ColorSchemeAssociationKind.SEPARATOR); + + // tab borders + defaultSchemeBundle.registerColorScheme(schemes.get("RuneLite Tab Border"), + ColorSchemeAssociationKind.TAB_BORDER, ComponentState.getActiveStates()); + + final SubstanceColorScheme watermarkScheme = schemes.get("RuneLite Watermark"); + + this.registerDecorationAreaSchemeBundle(defaultSchemeBundle, watermarkScheme, + DecorationAreaType.NONE); + + final SubstanceColorSchemeBundle decorationsSchemeBundle = new SubstanceColorSchemeBundle( + activeScheme, enabledScheme, enabledScheme); + decorationsSchemeBundle.registerColorScheme(enabledScheme, 0.5f, + ComponentState.DISABLED_UNSELECTED); + + // borders + decorationsSchemeBundle.registerColorScheme(borderDisabledSelectedScheme, + ColorSchemeAssociationKind.BORDER, ComponentState.DISABLED_SELECTED); + decorationsSchemeBundle.registerColorScheme(borderScheme, + ColorSchemeAssociationKind.BORDER); + + // marks + decorationsSchemeBundle.registerColorScheme(markActiveScheme, + ColorSchemeAssociationKind.MARK, ComponentState.getActiveStates()); + + // separators + final SubstanceColorScheme separatorDecorationsScheme = schemes + .get("RuneLite Decorations Separator"); + decorationsSchemeBundle.registerColorScheme(separatorDecorationsScheme, + ColorSchemeAssociationKind.SEPARATOR); + + final SubstanceColorScheme decorationsWatermarkScheme = schemes + .get("RuneLite Decorations Watermark"); + + this.registerDecorationAreaSchemeBundle(decorationsSchemeBundle, decorationsWatermarkScheme, + DecorationAreaType.TOOLBAR, DecorationAreaType.GENERAL, DecorationAreaType.FOOTER); + + final SubstanceColorSchemeBundle headerSchemeBundle = new SubstanceColorSchemeBundle(activeScheme, + enabledScheme, enabledScheme); + headerSchemeBundle.registerColorScheme(enabledScheme, 0.5f, + ComponentState.DISABLED_UNSELECTED); + + // borders + final SubstanceColorScheme headerBorderScheme = schemes.get("RuneLite Header Border"); + headerSchemeBundle.registerColorScheme(borderDisabledSelectedScheme, + ColorSchemeAssociationKind.BORDER, ComponentState.DISABLED_SELECTED); + headerSchemeBundle.registerColorScheme(headerBorderScheme, + ColorSchemeAssociationKind.BORDER); + // marks + headerSchemeBundle.registerColorScheme(markActiveScheme, ColorSchemeAssociationKind.MARK, + ComponentState.getActiveStates()); + + headerSchemeBundle.registerHighlightColorScheme(activeScheme, 0.7f, + ComponentState.ROLLOVER_UNSELECTED, ComponentState.ROLLOVER_ARMED, + ComponentState.ARMED); + headerSchemeBundle.registerHighlightColorScheme(activeScheme, 0.8f, + ComponentState.SELECTED); + headerSchemeBundle.registerHighlightColorScheme(activeScheme, 1.0f, + ComponentState.ROLLOVER_SELECTED); + + final SubstanceColorScheme headerWatermarkScheme = schemes.get("RuneLite Header Watermark"); + + this.registerDecorationAreaSchemeBundle(headerSchemeBundle, headerWatermarkScheme, + DecorationAreaType.PRIMARY_TITLE_PANE, DecorationAreaType.SECONDARY_TITLE_PANE, + DecorationAreaType.HEADER); + + setTabFadeStart(0.2); + setTabFadeEnd(0.9); + + // Add overlay painters to paint drop shadows along the bottom + // edges of toolbars and footers + this.addOverlayPainter(BottomShadowOverlayPainter.getInstance(), + DecorationAreaType.TOOLBAR); + this.addOverlayPainter(BottomShadowOverlayPainter.getInstance(), DecorationAreaType.FOOTER); + + // add an overlay painter to paint a dark line along the bottom + // edge of toolbars + final BottomLineOverlayPainter toolbarBottomLineOverlayPainter = new BottomLineOverlayPainter( + (SubstanceColorScheme scheme) -> scheme.getUltraDarkColor().darker()); + this.addOverlayPainter(toolbarBottomLineOverlayPainter, DecorationAreaType.TOOLBAR); + + // add an overlay painter to paint a dark line along the bottom + // edge of toolbars + final TopLineOverlayPainter toolbarTopLineOverlayPainter = new TopLineOverlayPainter( + (SubstanceColorScheme scheme) -> SubstanceColorUtilities + .getAlphaColor(scheme.getForegroundColor(), 32)); + this.addOverlayPainter(toolbarTopLineOverlayPainter, DecorationAreaType.TOOLBAR); + + // add an overlay painter to paint a bezel line along the top + // edge of footer + final TopBezelOverlayPainter footerTopBezelOverlayPainter = new TopBezelOverlayPainter( + (SubstanceColorScheme scheme) -> scheme.getUltraDarkColor().darker(), + (SubstanceColorScheme scheme) -> SubstanceColorUtilities + .getAlphaColor(scheme.getForegroundColor(), 32)); + this.addOverlayPainter(footerTopBezelOverlayPainter, DecorationAreaType.FOOTER); + + this.setTabFadeStart(0.18); + this.setTabFadeEnd(0.18); + + // Set button shaper to use "flat" design + this.buttonShaper = new ClassicButtonShaper() + { + @Override + public float getCornerRadius(AbstractButton button, float insets) + { + return 0; + } + }; + + this.watermark = null; + this.fillPainter = new FractionBasedFillPainter("RuneLite", + new float[]{0.0f, 0.5f, 1.0f}, + new ColorSchemeSingleColorQuery[]{ColorSchemeSingleColorQuery.ULTRALIGHT, + ColorSchemeSingleColorQuery.LIGHT, ColorSchemeSingleColorQuery.LIGHT}); + this.decorationPainter = new MatteDecorationPainter(); + this.highlightPainter = new ClassicHighlightPainter(); + this.borderPainter = new CompositeBorderPainter("RuneLite", new ClassicBorderPainter(), + new DelegateBorderPainter("RuneLite Inner", new ClassicBorderPainter(), 0x40FFFFFF, + 0x20FFFFFF, 0x00FFFFFF, + (SubstanceColorScheme scheme) -> scheme.tint(0.2f))); + } + + @Override + public String getDisplayName() + { + return NAME; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/skin/SubstanceRuneLiteLookAndFeel.java b/runelite-client/src/main/java/net/runelite/client/ui/skin/SubstanceRuneLiteLookAndFeel.java new file mode 100644 index 0000000000..86afea00a8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/skin/SubstanceRuneLiteLookAndFeel.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * 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.skin; + +import org.pushingpixels.substance.api.SubstanceLookAndFeel; + +public class SubstanceRuneLiteLookAndFeel extends SubstanceLookAndFeel +{ + public SubstanceRuneLiteLookAndFeel() + { + super(new ObsidianSkin()); + } +} \ No newline at end of file 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 ff5015917e..b6a66a35d6 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 @@ -25,6 +25,7 @@ package net.runelite.client.util; import java.awt.AWTException; +import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; @@ -79,6 +80,9 @@ public class SwingUtil ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false); JPopupMenu.setDefaultLightWeightPopupEnabled(false); + UIManager.put("Button.foreground", Color.WHITE); + UIManager.put("MenuItem.foreground", Color.WHITE); + // Do not render shadows under popups/tooltips. // Fixes black boxes under popups that are above the game applet. System.setProperty("jgoodies.popupDropShadowEnabled", "false"); @@ -210,7 +214,7 @@ public class SwingUtil result = JOptionPane.showConfirmDialog( frame, "Are you sure you want to exit?", "Exit", - JOptionPane .OK_CANCEL_OPTION, + JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); } } diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/skin/RuneLite.colorschemes b/runelite-client/src/main/resources/net/runelite/client/ui/skin/RuneLite.colorschemes new file mode 100644 index 0000000000..1d9ffee41b --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/ui/skin/RuneLite.colorschemes @@ -0,0 +1,166 @@ +# Copyright (c) 2018, Tomas Slusny +# 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. + +RuneLite Enabled { + kind=Dark + colorUltraLight=#232323 + colorExtraLight=#232323 + colorLight=#232323 + colorMid=#232323 + colorDark=#232323 + colorUltraDark=#232323 + colorForeground=#C6C6C6 +} + +RuneLite Active { + kind=Light + colorUltraLight=#4e4e4e + colorExtraLight=#4e4e4e + colorLight=#4e4e4e + colorMid=#232323 + colorDark=#232323 + colorUltraDark=#232323 + colorForeground=#000000 +} + +RuneLite Selected Disabled Border { + kind=Dark + colorUltraLight=#191919 + colorExtraLight=#191919 + colorLight=#191919 + colorMid=#191919 + colorDark=#191919 + colorUltraDark=#191919 + colorForeground=#C6C6C6 +} + +RuneLite Border { + kind=Dark + colorUltraLight=#191919 + colorExtraLight=#191919 + colorLight=#191919 + colorMid=#191919 + colorDark=#191919 + colorUltraDark=#191919 + colorForeground=#C6C6C6 +} + +RuneLite Tab Border { + kind=Light + colorUltraLight=#232323 + colorExtraLight=#232323 + colorLight=#232323 + colorMid=#232323 + colorDark=#232323 + colorUltraDark=#232323 + colorForeground=#232323 +} + +RuneLite Mark Active { + kind=Dark + colorUltraLight=#191919 + colorExtraLight=#191919 + colorLight=#191919 + colorMid=#191919 + colorDark=#191919 + colorUltraDark=#191919 + colorForeground=#191919 +} + +RuneLite Highlight { + kind=Light + colorUltraLight=#C6C6C6 + colorExtraLight=#C6C6C6 + colorLight=#C6C6C6 + colorMid=#C6C6C6 + colorDark=#C6C6C6 + colorUltraDark=#C6C6C6 + colorForeground=#191919 +} + +RuneLite Watermark { + kind=Light + colorUltraLight=#313131 + colorExtraLight=#313131 + colorLight=#313131 + colorMid=#313131 + colorDark=#313131 + colorUltraDark=#313131 + colorForeground=#C6C6C6 +} + +RuneLite Decorations Watermark { + kind=Light + colorUltraLight=#1e1e1e + colorExtraLight=#1e1e1e + colorLight=#1e1e1e + colorMid=#1e1e1e + colorDark=#1e1e1e + colorUltraDark=#1e1e1e + colorForeground=#1e1e1e +} + +RuneLite Separator { + kind=Dark + colorUltraLight=#232323 + colorExtraLight=#232323 + colorLight=#232323 + colorMid=#232323 + colorDark=#232323 + colorUltraDark=#232323 + colorForeground=#232323 +} + +RuneLite Decorations Separator { + kind=Dark + colorUltraLight=#232323 + colorExtraLight=#232323 + colorLight=#232323 + colorMid=#232323 + colorDark=#232323 + colorUltraDark=#232323 + colorForeground=#232323 +} + +RuneLite Header Watermark { + kind=Dark + colorUltraLight=#1e1e1e + colorExtraLight=#1e1e1e + colorLight=#1e1e1e + colorMid=#1e1e1e + colorDark=#1e1e1e + colorUltraDark=#1e1e1e + colorForeground=#C6C6C6 +} + +RuneLite Header Border { + kind=Dark + colorUltraLight=#1e1e1e + colorExtraLight=#1e1e1e + colorLight=#1e1e1e + colorMid=#1e1e1e + colorDark=#1e1e1e + colorUltraDark=#1e1e1e + colorForeground=#C6C6C6 +} \ No newline at end of file From e56e559ecdbf1e7da96d7be18c3ef17a89753613 Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Sun, 22 Apr 2018 03:47:52 +0100 Subject: [PATCH 02/15] 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 From ab7e969320bc392e2b734af147c5ff5a18b42f4c Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Sun, 22 Apr 2018 04:24:46 +0100 Subject: [PATCH 03/15] 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 --- .../grandexchange/GrandExchangeItemPanel.java | 28 +- .../grandexchange/GrandExchangeOfferSlot.java | 301 ++++++++++++------ .../GrandExchangeOffersPanel.java | 201 ++++++++++++ .../grandexchange/GrandExchangePanel.java | 59 ++-- .../grandexchange/GrandExchangePlugin.java | 12 +- .../GrandExchangeSearchPanel.java | 214 +++++++++---- .../ui/components/PluginErrorPanel.java | 2 +- .../runelite/client/util/StackFormatter.java | 63 +++- .../net/runelite/client/util/SwingUtil.java | 8 + .../plugins/grandexchange/arrow_left.png | Bin 0 -> 15918 bytes .../plugins/grandexchange/arrow_right.png | Bin 0 -> 15917 bytes .../client/plugins/grandexchange/search.png | Bin 388 -> 0 bytes .../client/util/StackFormatterTest.java | 27 ++ 13 files changed, 699 insertions(+), 216 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOffersPanel.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_left.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_right.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/search.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeItemPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeItemPanel.java index 24905da2c4..0a12675637 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeItemPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeItemPanel.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Seth + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,14 +33,17 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; -import javax.swing.border.LineBorder; import lombok.extern.slf4j.Slf4j; +import net.runelite.client.ui.ColorScheme; import net.runelite.client.game.AsyncBufferedImage; import net.runelite.client.util.LinkBrowser; import net.runelite.client.util.StackFormatter; +/** + * This panel displays an individual item result in the + * Grand Exchange search plugin. + */ @Slf4j class GrandExchangeItemPanel extends JPanel { @@ -52,7 +56,8 @@ class GrandExchangeItemPanel extends JPanel layout.setHgap(5); setLayout(layout); setToolTipText(name); - + setBackground(ColorScheme.MEDIUM_GRAY_COLOR); + Color background = getBackground(); addMouseListener(new MouseAdapter() @@ -60,7 +65,7 @@ class GrandExchangeItemPanel extends JPanel @Override public void mouseEntered(MouseEvent e) { - setBackground(getBackground().darker().darker()); + setBackground(getBackground().brighter()); } @Override @@ -76,11 +81,7 @@ class GrandExchangeItemPanel extends JPanel } }); - setBorder(new CompoundBorder - ( - new LineBorder(getBackground().brighter(), 1), - new EmptyBorder(5, 5, 5, 5) - )); + setBorder(new EmptyBorder(5, 5, 5, 0)); // Icon JLabel itemIcon = new JLabel(); @@ -97,6 +98,9 @@ class GrandExchangeItemPanel extends JPanel // Item name JLabel itemName = new JLabel(); + itemName.setForeground(Color.WHITE); + itemName.setMaximumSize(new Dimension(0, 0)); // to limit the label's size for + itemName.setPreferredSize(new Dimension(0, 0)); // items with longer names itemName.setText(name); rightPanel.add(itemName); @@ -110,13 +114,13 @@ class GrandExchangeItemPanel extends JPanel { gePriceLabel.setText("N/A"); } - gePriceLabel.setForeground(Color.GREEN); + gePriceLabel.setForeground(ColorScheme.GRAND_EXCHANGE_PRICE); rightPanel.add(gePriceLabel); // Alch price JLabel haPriceLabel = new JLabel(); haPriceLabel.setText(StackFormatter.formatNumber(haPrice.intValue()) + " alch"); - haPriceLabel.setForeground(Color.orange); + haPriceLabel.setForeground(ColorScheme.GRAND_EXCHANGE_ALCH); rightPanel.add(haPriceLabel); add(rightPanel, BorderLayout.CENTER); @@ -131,4 +135,4 @@ class GrandExchangeItemPanel extends JPanel LinkBrowser.browse(url); } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOfferSlot.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOfferSlot.java index 7e9409a26d..eab0023d68 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOfferSlot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOfferSlot.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, SomeoneWithAnInternetConnection + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,38 +29,73 @@ package net.runelite.client.plugins.grandexchange; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.awt.image.BufferedImage; +import java.io.IOException; import javax.annotation.Nullable; -import javax.swing.BorderFactory; -import javax.swing.Box; -import javax.swing.BoxLayout; +import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.border.TitledBorder; +import javax.swing.border.EmptyBorder; import lombok.extern.slf4j.Slf4j; import net.runelite.api.GrandExchangeOffer; import net.runelite.api.GrandExchangeOfferState; +import static net.runelite.api.GrandExchangeOfferState.CANCELLED_BUY; +import static net.runelite.api.GrandExchangeOfferState.CANCELLED_SELL; import static net.runelite.api.GrandExchangeOfferState.EMPTY; import net.runelite.api.ItemComposition; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.components.ThinProgressBar; import net.runelite.client.util.StackFormatter; +import net.runelite.client.util.SwingUtil; @Slf4j public class GrandExchangeOfferSlot extends JPanel { - private static final Color GE_INPROGRESS_ORANGE = new Color(0xd8, 0x80, 0x20).brighter(); - private static final Color GE_FINISHED_GREEN = new Color(0, 0x5f, 0); - private static final Color GE_CANCELLED_RED = new Color(0x8f, 0, 0); + private static final String FACE_CARD = "FACE_CARD"; + private static final String DETAILS_CARD = "DETAILS_CARD"; - private static final String INFO_CARD = "INFO_CARD"; - private static final String EMPTY_CARD = "EMPTY_CARD"; + private static final ImageIcon RIGHT_ARROW_ICON; + private static final ImageIcon LEFT_ARROW_ICON; + private final JPanel container = new JPanel(); private final CardLayout cardLayout = new CardLayout(); + private final JLabel itemIcon = new JLabel(); - private final TitledBorder itemName = BorderFactory.createTitledBorder("Nothing"); - private final JLabel offerState = new JLabel("Text so the label has height"); - private final JProgressBar progressBar = new JProgressBar(); + private final JLabel itemName = new JLabel(); + private final JLabel offerInfo = new JLabel(); + private final JLabel switchFaceViewIcon = new JLabel(); + + private final JLabel itemPrice = new JLabel(); + private final JLabel offerSpent = new JLabel(); + private final JLabel switchDetailsViewIcon = new JLabel(); + + private final ThinProgressBar progressBar = new ThinProgressBar(); + + private boolean showingFace = true; + + static + { + try + { + synchronized (ImageIO.class) + { + RIGHT_ARROW_ICON = new ImageIcon(ImageIO.read(GrandExchangeOfferSlot.class.getResourceAsStream("arrow_right.png"))); + LEFT_ARROW_ICON = new ImageIcon(ImageIO.read(GrandExchangeOfferSlot.class.getResourceAsStream("arrow_left.png"))); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } /** * This (sub)panel is used for each GE slot displayed @@ -72,123 +108,188 @@ public class GrandExchangeOfferSlot extends JPanel private void buildPanel() { - setBorder(BorderFactory.createCompoundBorder( - // Add a margin underneath each slot panel to space them out - BorderFactory.createEmptyBorder(0, 0, 3, 0), - itemName - )); + setLayout(new BorderLayout()); + setBackground(ColorScheme.DARK_GRAY_COLOR); + setBorder(new EmptyBorder(7, 0, 0, 0)); - // The default border color is kind of dark, so we change it to something lighter - itemName.setBorder(BorderFactory.createLineBorder(getBackground().brighter())); + final MouseListener ml = new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + super.mousePressed(mouseEvent); + switchPanel(); + } - progressBar.setStringPainted(true); + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + super.mouseEntered(mouseEvent); + container.setBackground(ColorScheme.MEDIUM_GRAY_COLOR.brighter()); + } - setLayout(cardLayout); + @Override + public void mouseExited(MouseEvent mouseEvent) + { + super.mouseExited(mouseEvent); + container.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); + } + }; - // Card for when the slot has an offer in it - JPanel infoCard = new JPanel(); - add(infoCard, INFO_CARD); - // Add padding to give the icon and progress bar room to breathe - infoCard.setBorder(BorderFactory.createEmptyBorder(0, 2, 2, 2)); + container.setLayout(cardLayout); + container.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); - infoCard.setLayout(new BoxLayout(infoCard, BoxLayout.X_AXIS)); - // Icon on the left - infoCard.add(itemIcon); + JPanel faceCard = new JPanel(); + faceCard.setOpaque(false); + faceCard.setLayout(new BorderLayout()); + faceCard.addMouseListener(ml); - // Info on the right - JPanel offerStatePanel = new JPanel(); - offerStatePanel.setLayout(new BoxLayout(offerStatePanel, BoxLayout.Y_AXIS)); - offerStatePanel.add(offerState); - offerStatePanel.add(progressBar); - infoCard.add(offerStatePanel); + itemIcon.setVerticalAlignment(JLabel.CENTER); + itemIcon.setHorizontalAlignment(JLabel.CENTER); + itemIcon.setPreferredSize(new Dimension(45, 45)); - // Card for when the slot is empty - JPanel emptySlotCard = new JPanel(); - add(emptySlotCard, EMPTY_CARD); - // Counteract the height lost to the text at the top of the TitledBorder - int itemNameBorderHeight = itemName.getBorderInsets(this).top; - emptySlotCard.setBorder(BorderFactory.createEmptyBorder(0, 0, (itemNameBorderHeight - 1) / 2, 0)); - // Center the "Empty" label horizontally - emptySlotCard.setLayout( new BoxLayout(emptySlotCard, BoxLayout.X_AXIS)); - emptySlotCard.add(Box.createHorizontalGlue()); - emptySlotCard.add(new JLabel(getNameForState(EMPTY)), BorderLayout.CENTER); - emptySlotCard.add(Box.createHorizontalGlue()); + itemName.setForeground(Color.WHITE); + itemName.setVerticalAlignment(JLabel.BOTTOM); + itemName.setFont(FontManager.getRunescapeSmallFont()); - cardLayout.show(this, EMPTY_CARD); + offerInfo.setForeground(ColorScheme.LIGHT_GRAY_COLOR); + offerInfo.setVerticalAlignment(JLabel.TOP); + offerInfo.setFont(FontManager.getRunescapeSmallFont()); + + switchFaceViewIcon.setIcon(RIGHT_ARROW_ICON); + switchFaceViewIcon.setVerticalAlignment(JLabel.CENTER); + switchFaceViewIcon.setHorizontalAlignment(JLabel.CENTER); + switchFaceViewIcon.setPreferredSize(new Dimension(30, 45)); + + JPanel offerFaceDetails = new JPanel(); + offerFaceDetails.setOpaque(false); + offerFaceDetails.setLayout(new GridLayout(2, 1, 0, 2)); + + offerFaceDetails.add(itemName); + offerFaceDetails.add(offerInfo); + + faceCard.add(offerFaceDetails, BorderLayout.CENTER); + faceCard.add(itemIcon, BorderLayout.WEST); + faceCard.add(switchFaceViewIcon, BorderLayout.EAST); + + JPanel detailsCard = new JPanel(); + detailsCard.setOpaque(false); + detailsCard.setLayout(new BorderLayout()); + detailsCard.setBorder(new EmptyBorder(0, 15, 0, 0)); + detailsCard.addMouseListener(ml); + + itemPrice.setForeground(Color.WHITE); + itemPrice.setVerticalAlignment(JLabel.BOTTOM); + itemPrice.setFont(FontManager.getRunescapeSmallFont()); + + offerSpent.setForeground(Color.WHITE); + offerSpent.setVerticalAlignment(JLabel.TOP); + offerSpent.setFont(FontManager.getRunescapeSmallFont()); + + switchDetailsViewIcon.setIcon(LEFT_ARROW_ICON); + switchDetailsViewIcon.setVerticalAlignment(JLabel.CENTER); + switchDetailsViewIcon.setHorizontalAlignment(JLabel.CENTER); + switchDetailsViewIcon.setPreferredSize(new Dimension(30, 45)); + + JPanel offerDetails = new JPanel(); + offerDetails.setOpaque(false); + offerDetails.setLayout(new GridLayout(2, 1)); + + offerDetails.add(itemPrice); + offerDetails.add(offerSpent); + + detailsCard.add(offerDetails, BorderLayout.CENTER); + detailsCard.add(switchDetailsViewIcon, BorderLayout.EAST); + + container.add(faceCard, FACE_CARD); + container.add(detailsCard, DETAILS_CARD); + + cardLayout.show(container, FACE_CARD); + + add(container, BorderLayout.CENTER); + add(progressBar, BorderLayout.SOUTH); } - + void updateOffer(ItemComposition offerItem, BufferedImage itemImage, @Nullable GrandExchangeOffer newOffer) { if (newOffer == null || newOffer.getState() == EMPTY) { - cardLayout.show(this, EMPTY_CARD); - itemName.setTitle("Nothing"); + return; } else { - cardLayout.show(this, INFO_CARD); + cardLayout.show(container, FACE_CARD); - itemName.setTitle(offerItem.getName()); + itemName.setText(offerItem.getName()); + itemIcon.setIcon(new ImageIcon(itemImage)); - boolean shouldStack = offerItem.isStackable() || newOffer.getTotalQuantity() > 1; - ImageIcon newItemIcon = new ImageIcon(itemImage); - itemIcon.setIcon(newItemIcon); + boolean buying = newOffer.getState() == GrandExchangeOfferState.BOUGHT + || newOffer.getState() == GrandExchangeOfferState.BUYING + || newOffer.getState() == GrandExchangeOfferState.CANCELLED_BUY; - offerState.setText(getNameForState(newOffer.getState()) - + " at " - + StackFormatter.formatNumber(newOffer.getState() == GrandExchangeOfferState.BOUGHT ? (newOffer.getSpent() / newOffer.getTotalQuantity()) : newOffer.getPrice()) - + (newOffer.getTotalQuantity() > 1 ? " gp ea" : " gp")); + String offerState = (buying ? "Bought " : "Sold ") + + StackFormatter.quantityToRSDecimalStack(newOffer.getQuantitySold()) + " / " + + StackFormatter.quantityToRSDecimalStack(newOffer.getTotalQuantity()); - progressBar.setMaximum(newOffer.getTotalQuantity()); + offerInfo.setText(offerState); + + itemPrice.setText(htmlLabel("Price each: ", newOffer.getPrice() + "")); + + String action = buying ? "Spent: " : "Received: "; + + offerSpent.setText(htmlLabel(action, StackFormatter.formatNumber(newOffer.getSpent()) + " / " + + StackFormatter.formatNumber(newOffer.getPrice() * newOffer.getTotalQuantity()))); + + progressBar.setForeground(getProgressColor(newOffer)); + progressBar.setMaximumValue(newOffer.getTotalQuantity()); progressBar.setValue(newOffer.getQuantitySold()); - progressBar.setBackground(getColorForState(newOffer.getState())); - progressBar.setString(newOffer.getQuantitySold() + "/" + newOffer.getTotalQuantity()); + progressBar.update(); + + /* Couldn't set the tooltip for the container panel as the children override it, so I'm setting + * the tooltips on the children instead. */ + for (Component c : container.getComponents()) + { + if (c instanceof JPanel) + { + JPanel panel = (JPanel) c; + panel.setToolTipText(htmlTooltip(((int) progressBar.getPercentage()) + "%")); + } + } } + + revalidate(); repaint(); } - private String getNameForState(GrandExchangeOfferState state) + private String htmlTooltip(String value) { - switch (state) - { - case CANCELLED_BUY: - return "Buying cancelled"; - case CANCELLED_SELL: - return "Selling cancelled"; - case BUYING: - return "Buying"; - case BOUGHT: - return "Bought"; - case SELLING: - return "Selling"; - case SOLD: - return "Sold"; - case EMPTY: - default: - return "Empty"; - - } + return "Progress: " + value + ""; } - private Color getColorForState(GrandExchangeOfferState state) + private String htmlLabel(String key, String value) { - switch (state) - { - case CANCELLED_BUY: - case CANCELLED_SELL: - return GE_CANCELLED_RED; - case BUYING: - case SELLING: - return GE_INPROGRESS_ORANGE; - case BOUGHT: - case SOLD: - return GE_FINISHED_GREEN; - case EMPTY: - default: - return null; - } + return "" + key + "" + value + ""; } + 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; + } } - diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOffersPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOffersPanel.java new file mode 100644 index 0000000000..fb13394a05 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOffersPanel.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2018, SomeoneWithAnInternetConnection + * 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.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); + } + + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePanel.java index 3f84da8e01..b8bf685117 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePanel.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, SomeoneWithAnInternetConnection + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,58 +27,58 @@ package net.runelite.client.plugins.grandexchange; import java.awt.BorderLayout; -import java.awt.image.BufferedImage; import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; -import javax.swing.BoxLayout; import javax.swing.JPanel; -import javax.swing.JTabbedPane; +import javax.swing.border.EmptyBorder; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; -import net.runelite.api.GrandExchangeOffer; -import net.runelite.api.ItemComposition; import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.materialtabs.MaterialTab; +import net.runelite.client.ui.components.materialtabs.MaterialTabGroup; @Slf4j class GrandExchangePanel extends PluginPanel { - private static final int MAX_OFFERS = 8; + + // this panel will hold either the ge search panel or the ge offers panel + private final JPanel display = new JPanel(); + + private final MaterialTabGroup tabGroup = new MaterialTabGroup(display); + private final MaterialTab searchTab; @Getter private GrandExchangeSearchPanel searchPanel; - - private GrandExchangeOfferSlot[] offerSlotPanels = new GrandExchangeOfferSlot[MAX_OFFERS]; - - private JPanel offerPanel = new JPanel(); - - private JTabbedPane tabbedPane = new JTabbedPane(); + @Getter + private GrandExchangeOffersPanel offersPanel; @Inject GrandExchangePanel(Client client, ItemManager itemManager, ScheduledExecutorService executor) { - setLayout(new BorderLayout()); - add(tabbedPane, BorderLayout.NORTH); + super(false); - // Offer Panel - offerPanel.setLayout(new BoxLayout(offerPanel, BoxLayout.Y_AXIS)); - for (int i = 0; i < offerSlotPanels.length; ++i) - { - offerSlotPanels[i] = new GrandExchangeOfferSlot(); - offerPanel.add(offerSlotPanels[i]); - } + setLayout(new BorderLayout()); + setBackground(ColorScheme.DARK_GRAY_COLOR); // Search Panel searchPanel = new GrandExchangeSearchPanel(client, itemManager, executor); - tabbedPane.addTab("Offers", offerPanel); - tabbedPane.addTab("Search", searchPanel); - } + //Offers Panel + offersPanel = new GrandExchangeOffersPanel(client, itemManager, executor); - void updateOffer(ItemComposition item, BufferedImage itemImage, GrandExchangeOffer newOffer, int slot) - { - offerSlotPanels[slot].updateOffer(item, itemImage, newOffer); + MaterialTab offersTab = new MaterialTab("Offers", tabGroup, offersPanel); + searchTab = new MaterialTab("Search", tabGroup, searchPanel); + + tabGroup.setBorder(new EmptyBorder(5, 0, 0, 0)); + tabGroup.addTab(offersTab); + tabGroup.addTab(searchTab); + tabGroup.select(offersTab); // selects the default selected tab + + add(tabGroup, BorderLayout.NORTH); + add(display, BorderLayout.CENTER); } void showSearch() @@ -87,7 +88,7 @@ class GrandExchangePanel extends PluginPanel return; } - tabbedPane.setSelectedComponent(searchPanel); + tabGroup.select(searchTab); revalidate(); } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java index 1c10fbcd7d..21078d646f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java @@ -44,6 +44,7 @@ import net.runelite.api.ItemComposition; import net.runelite.api.MenuEntry; import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.FocusChanged; +import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GrandExchangeOfferChanged; import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.widgets.WidgetID; @@ -161,7 +162,16 @@ public class GrandExchangePlugin extends Plugin ItemComposition offerItem = itemManager.getItemComposition(offer.getItemId()); boolean shouldStack = offerItem.isStackable() || offer.getTotalQuantity() > 1; BufferedImage itemImage = itemManager.getImage(offer.getItemId(), offer.getTotalQuantity(), shouldStack); - SwingUtilities.invokeLater(() -> panel.updateOffer(offerItem, itemImage, offerEvent.getOffer(), offerEvent.getSlot())); + SwingUtilities.invokeLater(() -> panel.getOffersPanel().updateOffer(offerItem, itemImage, offerEvent.getOffer(), offerEvent.getSlot())); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) + { + if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN) + { + panel.getOffersPanel().resetOffers(); + } } @Subscribe 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 6372ea82c6..628658c179 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 @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Seth + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,9 +27,10 @@ package net.runelite.client.plugins.grandexchange; import com.google.common.base.Strings; import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.GridLayout; -import java.awt.image.BufferedImage; +import java.awt.CardLayout; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -36,8 +38,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; import javax.imageio.ImageIO; import javax.swing.ImageIcon; -import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import lombok.extern.slf4j.Slf4j; @@ -45,26 +47,69 @@ import net.runelite.api.Client; import net.runelite.api.ItemComposition; import net.runelite.client.game.AsyncBufferedImage; import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.components.IconTextField; +import net.runelite.client.ui.components.PluginErrorPanel; import net.runelite.http.api.item.Item; import net.runelite.http.api.item.ItemPrice; import net.runelite.http.api.item.SearchResult; +/** + * This panel holds the search section of the Grand Exchange Plugin. + * It should display a search bar and either item results or a error panel. + */ @Slf4j class GrandExchangeSearchPanel extends JPanel { - private static final List ITEMS_LIST = new ArrayList<>(); + private static final String ERROR_PANEL = "ERROR_PANEL"; + private static final String RESULTS_PANEL = "RESULTS_PANEL"; + + private static final ImageIcon SEARCH_ICON; + private static final ImageIcon LOADING_ICON; + private static final ImageIcon ERROR_ICON; + + private final GridBagConstraints constraints = new GridBagConstraints(); + private final CardLayout cardLayout = new CardLayout(); private final Client client; private final ItemManager itemManager; private final ScheduledExecutorService executor; - private ImageIcon search; + private final IconTextField searchBox = new IconTextField(); - private IconTextField searchBox = new IconTextField(); - private JPanel container = new JPanel(); - private JPanel searchItemsPanel = new JPanel(); - private JLabel searchingLabel = new JLabel(); + /* The main container, this holds the search bar and the center panel */ + private final JPanel container = new JPanel(); + + /* The results container, this will hold all the individual ge item panels */ + private final JPanel searchItemsPanel = new JPanel(); + + /* The center panel, this holds either the error panel or the results container */ + private final JPanel centerPanel = new JPanel(cardLayout); + + /* The error panel, this displays an error message */ + private final PluginErrorPanel errorPanel = new PluginErrorPanel(); + + /* The results wrapper, this scrolling panel wraps the results container */ + private JScrollPane resultsWrapper; + + private List itemsList = new ArrayList<>(); + + static + { + try + { + synchronized (ImageIO.class) + { + SEARCH_ICON = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search_darker.png"))); + LOADING_ICON = new ImageIcon(IconTextField.class.getResource("loading_spinner.gif")); + ERROR_ICON = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("error.png"))); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } GrandExchangeSearchPanel(Client client, ItemManager itemManager, ScheduledExecutorService executor) { @@ -77,38 +122,54 @@ class GrandExchangeSearchPanel extends JPanel void init() { setLayout(new BorderLayout()); - container.setLayout(new BorderLayout()); + setBackground(ColorScheme.DARK_GRAY_COLOR); - // Search Box - try - { - BufferedImage icon; - synchronized (ImageIO.class) - { - icon = ImageIO.read(GrandExchangePlugin.class.getResourceAsStream("search.png")); - } - search = new ImageIcon(icon); - } - catch (IOException e) - { - log.warn("Failed to read icon", e); - } + container.setLayout(new BorderLayout(5, 5)); + container.setBorder(new EmptyBorder(10, 10, 10, 10)); + container.setBackground(ColorScheme.DARK_GRAY_COLOR); - searchBox.setIcon(search); + searchBox.setPreferredSize(new Dimension(100, 30)); + searchBox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); + searchBox.setHoverBackgroundColor(ColorScheme.MEDIUM_GRAY_COLOR.brighter()); + searchBox.setIcon(SEARCH_ICON); searchBox.addActionListener(e -> executor.execute(() -> priceLookup(false))); + searchItemsPanel.setLayout(new GridBagLayout()); + searchItemsPanel.setBackground(ColorScheme.DARK_GRAY_COLOR); + + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.weightx = 1; + constraints.gridx = 0; + constraints.gridy = 0; + + /* This panel wraps the results panel and guarantees the scrolling behaviour */ + JPanel wrapper = new JPanel(new BorderLayout()); + wrapper.setBackground(ColorScheme.DARK_GRAY_COLOR); + wrapper.add(searchItemsPanel, BorderLayout.NORTH); + + resultsWrapper = new JScrollPane(wrapper); + resultsWrapper.setBackground(ColorScheme.DARK_GRAY_COLOR); + resultsWrapper.getVerticalScrollBar().setPreferredSize(new Dimension(12, 0)); + resultsWrapper.getVerticalScrollBar().setBorder(new EmptyBorder(0, 5, 0, 0)); + resultsWrapper.setVisible(false); + + /* This panel wraps the error panel and limits its height */ + JPanel errorWrapper = new JPanel(new BorderLayout()); + errorWrapper.setBackground(ColorScheme.DARK_GRAY_COLOR); + errorWrapper.add(errorPanel, BorderLayout.NORTH); + + errorPanel.setContent("Grand Exchange Search", + "Here you can search for an item by its name to find price information."); + + centerPanel.add(resultsWrapper, RESULTS_PANEL); + centerPanel.add(errorWrapper, ERROR_PANEL); + + cardLayout.show(centerPanel, ERROR_PANEL); + container.add(searchBox, BorderLayout.NORTH); + container.add(centerPanel, BorderLayout.CENTER); - // Searching label - searchingLabel.setHorizontalAlignment(JLabel.CENTER); - searchingLabel.setForeground(Color.YELLOW); - - // Items Panel - searchItemsPanel.setLayout(new GridLayout(0, 1, 0, 3)); - searchItemsPanel.setBorder(new EmptyBorder(3, 0, 0, 0)); - - container.add(searchItemsPanel, BorderLayout.SOUTH); - add(container, BorderLayout.NORTH); + add(container, BorderLayout.CENTER); } void priceLookup(String item) @@ -129,7 +190,9 @@ class GrandExchangeSearchPanel extends JPanel // Input is not empty, add searching label searchItemsPanel.removeAll(); - showSearchString("Searching..."); + searchBox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); + searchBox.setEditable(false); + searchBox.setIcon(LOADING_ICON); SearchResult result; @@ -140,13 +203,19 @@ class GrandExchangeSearchPanel extends JPanel catch (ExecutionException ex) { log.warn("Unable to search for item {}", lookup, ex); - showSearchString("Error performing search"); + searchBox.setIcon(ERROR_ICON); + searchBox.setEditable(true); + errorPanel.setContent("Error fetching results", "An error occured why trying to fetch item data, please try again later."); + cardLayout.show(centerPanel, ERROR_PANEL); return; } + itemsList.clear(); if (result != null && !result.getItems().isEmpty()) { + cardLayout.show(centerPanel, RESULTS_PANEL); + for (Item item : result.getItems()) { int itemId = item.getId(); @@ -169,7 +238,7 @@ class GrandExchangeSearchPanel extends JPanel AsyncBufferedImage itemImage = itemManager.getImage(itemId); - ITEMS_LIST.add(new GrandExchangeItems(itemImage, item.getName(), itemId, itemPrice != null ? itemPrice.getPrice() : 0, itemComp.getPrice() * 0.6)); + itemsList.add(new GrandExchangeItems(itemImage, item.getName(), itemId, itemPrice != null ? itemPrice.getPrice() : 0, itemComp.getPrice() * 0.6)); // If using hotkey to lookup item, stop after finding match. if (exactMatch && item.getName().equalsIgnoreCase(lookup)) @@ -178,44 +247,51 @@ class GrandExchangeSearchPanel extends JPanel } } } + else + { + searchBox.setIcon(ERROR_ICON); + errorPanel.setContent("No results found.", "No items were found with that name, please try again."); + cardLayout.show(centerPanel, ERROR_PANEL); + } SwingUtilities.invokeLater(() -> { - if (ITEMS_LIST.isEmpty()) + int index = 0; + for (GrandExchangeItems item : itemsList) { - showSearchString("No results found."); - } - else - { - for (GrandExchangeItems item : ITEMS_LIST) + GrandExchangeItemPanel panel = new GrandExchangeItemPanel(item.getIcon(), item.getName(), + item.getItemId(), item.getGePrice(), item.getHaPrice()); + + /* + Add the first item directly, wrap the rest with margin. This margin hack is because + gridbaglayout does not support inter-element margins. + */ + if (index++ > 0) { - GrandExchangeItemPanel panel = new GrandExchangeItemPanel(item.getIcon(), item.getName(), - item.getItemId(), item.getGePrice(), item.getHaPrice()); - - searchItemsPanel.add(panel); + JPanel marginWrapper = new JPanel(new BorderLayout()); + marginWrapper.setBackground(ColorScheme.DARK_GRAY_COLOR); + marginWrapper.setBorder(new EmptyBorder(5, 0, 0, 0)); + marginWrapper.add(panel, BorderLayout.NORTH); + searchItemsPanel.add(marginWrapper, constraints); } + else + { + searchItemsPanel.add(panel, constraints); + } + + constraints.gridy++; + } - // Remove searching label after search is complete - showSearchString(null); - ITEMS_LIST.clear(); + // remove focus from the search bar + searchItemsPanel.requestFocusInWindow(); + searchBox.setEditable(true); + + // Remove searching label after search is complete + if (!itemsList.isEmpty()) + { + searchBox.setIcon(SEARCH_ICON); } }); } - private void showSearchString(String str) - { - if (str != null) - { - remove(searchingLabel); - searchingLabel.setText(str); - add(searchingLabel, BorderLayout.CENTER); - } - else - { - remove(searchingLabel); - } - - revalidate(); - repaint(); - } -} +} \ 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 index 779253cff0..e7b2fad971 100644 --- 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 @@ -46,7 +46,7 @@ public class PluginErrorPanel extends JPanel public PluginErrorPanel() { setOpaque(false); - setBorder(new EmptyBorder(50, 0, 0, 0)); + setBorder(new EmptyBorder(50, 10, 0, 10)); setLayout(new BorderLayout()); noResultsTitle.setForeground(Color.WHITE); diff --git a/runelite-client/src/main/java/net/runelite/client/util/StackFormatter.java b/runelite-client/src/main/java/net/runelite/client/util/StackFormatter.java index 607da65d91..777688f84a 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/StackFormatter.java +++ b/runelite-client/src/main/java/net/runelite/client/util/StackFormatter.java @@ -128,6 +128,61 @@ public class StackFormatter } } + /** + * Convert a quantity to stack size as it would + * appear in RuneScape. (with decimals) + *

+ * This differs from quantityToRSStack in that it displays + * decimals. Ex: 27100 is 27,1k (not 27k) + *

+ * This uses the NumberFormat singleton instead of the + * NUMBER_FORMATTER variable to ensure the UK locale. + * + * @param quantity The quantity to convert. + * @return The stack size as it would appear in RS, with decimals, + * with K after 100,000 and M after 10,000,000 + */ + public static String quantityToRSDecimalStack(int quantity) + { + + if (quantity < 10_000) + { + return Integer.toString(quantity); + } + else if (quantity < 1_000_000) + { + if (quantity % 1000 == 0) + { + return quantity / 1000 + "K"; + } + return NUMBER_FORMATTER.format(quantity).substring(0, Integer.toString(quantity).length() - 1) + "K"; + } + else if (quantity < 10_000_000) + { + if (quantity % 1_000_000 == 0) + { + return quantity / 1_000_000 + "M"; + } + return NUMBER_FORMATTER.format(quantity).substring(0, Integer.toString(quantity).length() - 4) + "M"; + } + else if (quantity < 1_000_000_000) + { + if (quantity % 1_000_000 == 0) + { + return quantity / 1_000_000 + "M"; + } + return NUMBER_FORMATTER.format(quantity).substring(0, Integer.toString(quantity).length() - 4) + "M"; + } + else + { + if (quantity % 1_000_000_000 == 0) + { + return quantity / 1_000_000_000 + "B"; + } + return NUMBER_FORMATTER.format(quantity).substring(0, Integer.toString(quantity).length() - 7) + "B"; + } + } + /** * Converts a string representation of a stack * back to (close to) it's original value. @@ -147,8 +202,8 @@ public class StackFormatter * * @param number the long number to format * @return the formatted String - * @exception ArithmeticException if rounding is needed with rounding - * mode being set to RoundingMode.UNNECESSARY + * @throws ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY * @see java.text.Format#format */ public static String formatNumber(final long number) @@ -161,8 +216,8 @@ public class StackFormatter * * @param number the double number to format * @return the formatted String - * @exception ArithmeticException if rounding is needed with rounding - * mode being set to RoundingMode.UNNECESSARY + * @throws ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY * @see java.text.Format#format */ public static String formatNumber(double number) 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 a83cd7e24e..674ebe07dd 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 @@ -94,6 +94,14 @@ public class SwingUtil System.setProperty("sun.awt.noerasebackground", "true"); } + /** + * Converts a given color to it's hexidecimal equivalent. + */ + public static String toHexColor(Color color) + { + return "#" + Integer.toHexString(color.getRGB()).substring(2); + } + /** * Safely sets Swing theme * diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_left.png b/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_left.png new file mode 100644 index 0000000000000000000000000000000000000000..480358e6a1c6da1f527110a7605402f6b162c0ca GIT binary patch literal 15918 zcmeI3e^?Xe9l*~g(kbdzYO63?jZvqRCU=(*Le3Dx1SMNz`4KBns$A|aa2j$k2@ud> zk+#;^R%@-)U#Qz~6RWOWwR38rDz3UJ57kFIZHHL!*TmXci>=P=y@dSW1@7^A+CBT{ z?s<~C@B96A_j%v%_q~5^dFHH{BcgvEjUdPfQ-(1coFCzTLk5HYdw-kpIXDegtcCOxlwST-%39B!f9)MR9r`L74@*JT;2RA3y~ zjC{s4KONXeYTQ+6OqQ*3#`2|G>+9>6QopRqZkX<57MkWe-d$QW*Hx93R+VKn&5s7g zr+@koIOf*gdnBs+$S1legJkwYPu9Hh@sy}vT8`}qqPUdt7j5vGt8HEUVnC9Wd$0A zzcl$b8`i8}W3ZgIR5jG)JonqVJ7eD2{=4RFw=))PK575r$%9jVogj&;S@QDAS6?5q z8((M7XV%(pP^4o!-CT70{P*=IMw}m6HnnTc)K5w`UO9cOpfmfz`WvGzkG&Cd;c92+ zwXQ4FhZyU-;+>~FtTBOL(ysb z&V!cjt<{ZLyN~VGHZs%ywQX-gSKHM3{nz)uxrkVF_V)8#K2;X8x}{@v{H<+uZ-2|a zeEsQ`y!tMaTFxCUJ%6V0AAfv5 z-gWDUeDYr0@xi&Q1MaR}qZh6D_Gs6JJ-%~k?S|@QcUNvqIqLrWuCe==>JJqs?`+!S zyzyyN>x!7#O9fTV*`4`kZr|2?_|NzDeX;oR>6EQ^TYt7zxA^X7_e@`&QnfVCn|QFG z`&##yl^^7^rQ9#P^KSd8@6dnGyVcm`8-G)E_3YYI`;rOwci#7nLk1s`o*44`HLVCj zpsXd=lWTsOptuqRNpn_4;Vp53ib4=ws@F+U3mA{o$`rAV6xmn%8)Z_KPLa(~n=!M~ zz!b9?l`bZya+ZavTtI1QS*ku-=OsXZ62?PHy(M;soA9Q{{Ba3z%@3n8so%u2AVrqO zCzR%zGo=R3#YokPBsqm8X`~vh0#|D^iJGURN(@({m}73C@g=PE*RtyYU-N>r(o0}Hvk!r>vka)*0DAV@Hd zk#SQl*6Cq6hm?;?TDekBicH2Q3LQPqRpJaKa=3fg0g9-XbfUNdL;G{0sgRGe)MfYI z8cm^$ohe})9yjpA`}K1MyI9D*SIUrjz{Frq%;x@`gZEof(r;?FC%p_H=t0^mZP3$g zscT@;gE1|DTXe^2jWZQ;r36^Yjjc<+pt9l(v(1l;^fAuvNa@T`Qv#gHD( zW#KrxKCsHd8v)n{_*EsHGKY20T)8`5hr%x5`3^^8jHHLr1J7hRmMF*77ED8^wS-EY zfZ+s&MFa_0LNUx7&DtvZ#7HJEHKEo-#)zIlxN!lZ9_-CO(GL6zO9gFj~m-}Iv)%{Aby1Vz&Xl`8HZ4X215C*1`gupY@Tw`#iSL174jg> zBM`N>8ysGT^4p1>{U1z(n=Jcb)9_$b^+TosYI6U-X$Y@ks+e>XF|-~HF5uw6gUwuE zK6_VIuv+_8qK@C+2vEgfMUnpM0e!U3SU7XQK9{dXd#T@Eqr||0;{1P)Yor=>%@cNw zRKu=e&)E*vqX#|6f1}WFgcrXF2X=@Y@V?0ClhkUJT8F~Jutz5AW$d{|7W82^-%s%5 z9v@iIuVdaojuDC;$G}uYyxI9{Ffa<<8^J3p8hU5#Q=XwN_G4azpXkSo3dItWKtO~= z5tlF@NGsxkfC!5sE@3{9R>TDX5f(*U!h9gDhzkNDEQ+{<`9N9`7X(CD6mbdjfwUqn z2#ByK;u7WqX+>NR5MfcoCCmrXint&k!lH;vm=B~CaX~P5f=nRSQK#y^MSM?E(nOQDB=?418GHE5D;Ne z#3jrJ(u%ksAi|=EOPCL&6>&j8ghdgTFds-O;(~w(iy|&zK9E+#1pyHjMO?ysAgzcC z0wOF5;))J`1Xs-`J#p{OFqA4Md9!vvB*Tp X%)ui*+c=T`TGf<3%eZTL;nM#Ctaa;x literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_right.png b/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_right.png new file mode 100644 index 0000000000000000000000000000000000000000..63ab23f273cfcdbd6ff203ecf37900db0eb23d00 GIT binary patch literal 15917 zcmeI3dvw#*8NkJH(m^QQ3@yZC1!G_(q_!kK;#fEi!4AZ24S5(yXn4r7oCs{o*iPaE z76>V&^Z|@g(((wUBWyr{wF5dz8G#a37?4pout7T>O?Zs}9fi=r@UHB{c5V_K&S`h{ zk93YLefN7wzk9#$>K`r69zU*6!p{>B1nFbWGUbAEU+&j44*Wm-^VqMzsh20Kz=t5c z`*FV>$f`Ao2qH>kjK=KjV%E?4idm1;Y&1$eUe>`lX#@$JrDn{iy>RtX(~)8I_1W3h z+@?WIi4w7-uD-Ii>aD8xs<>kazWa~$xU_x|ca5f_@AMGO-E*k^i~9O{Pr;cJ4Ts;U zQrE94%(fl?1M{n@_Kz68GZnaA>@5-*E6U9oy+k7FNs*{JE4MUs-GjR>&03}cIra6t9jTrzV+ZyeY{aL@rjYuuYEBh{=3^5uMHXPWpX61G;lZh{;_4B|GxUyOYt|p zeg5sFlQxI=vZ8ydj${3=-_sh;=h(l?h3+>XEJ zoI#g~3EJpD-AUv?%JCimMsb92!t=hnT} z*7a5Ua<(7duG>eC`RB%6$t~wc)$O^l=j}PfoHKW)wggo<^zz20dG*Fi zjRkdo7;s#d-r6{~B|fM3r~xg74`*)rPPwkRBspc<+>fW&-fBF1sN~$~eSiPmjwJ7$ zgYxHg;qFh)=6vRB**0L#ihGAzR__d+&A4c+TJ&J)nzTc{uO66M537D#e*FI0wVs<_ z#-DgYTywdw(lfDn%IUjzwSW4@$Gg9td*xKx`UfX|x>7&)!QUR5zd5ODY?wZ5Z(-~8 z)`3eu$vdC+XvY1IE}r}z{nzw6`&xoSZmX`HS()yfH}uigN5R2J+yUvao*P!2KoA0D ztoiZY_i6JSv*F% zn8~W}(s>o*tyIM0+-6`{rmwNl!4$P5YKO7FEh zh+Naywsb&BlNI~@9s)(n%E}aFDh2B;LUEl=hhj=psgwf?xv$*qCj)Y~Z)iA3dmIz( zqr8mA&#-PO7niiLCH^#-j7tkV=V|X^5%&%$BkFDwgE_HSx^`~AUze-P)INV^DL~MMv_sl}dl%ZWcy`OPJC#ORsPSfqZ(*dR%H%=3F3oV844C%qM5=Jjg`dP1) zWu1obDvNFeU?1RCm2|{p#%*WId`Wr~c8Sh+G#YIp{j>pirpht39M@PeEuqm7Dorwm z6Brf~By0)Au&{Q$j7dz0&|)Rb zZl9lYQ?%J+00Rn!u@iP&n`%=kZE{-Wu*q?oS|cY(wOX!G*(jTaN~P>7Dl`ukORRgR z&?c5D;R+6f4)-QIOM&pwCsR>Mt&L8_zAm2d1&>?3mI4V24q35vEhB zEXD^Ge0fYEfN}^m$2@v=$I*6$lL<9B59#&MTnVSi+G{w{6(dcL^9i*Af~2_iXrQxC@ZGjd=(lF6|(9+h>vyn%SbPsQ3O`V<1~+O z)Q)a&cs_owqzx$=jY^|O;bGV#n+ed)d=mruu#f8}cxsyu ztmx9QpgYGHMVn)Is$$;kLNyp31@DdEl@*P=vvw-aNEiDtFQQNMV@8Ey2}vLz{Gx!1 zpAV!Ja6v%$MFAH-A4n_Uf`IUg0xo_&kXFD20pS+~T>N|>t$+&x!Y>NA`1wFu0T%>> zUlefh^MSMiE(i#}DB$Af18D_Z5Dvd7_(cI1KOaad z;DUhgivliwK9E+x1p(m~1zh}mAgzE40>Uo}xcK=%S^*aXgkKbJ@$-SS0xk#$zbN41 z=L2a4To4d`QNYE|2hs|-ARzprfQz3Gq!n;MK=?%g7e60JE8v2F@QVU2em;;^zy$%} z7kP0dM85*1-Qd&DGVqP(1kKS8!S|t3Dl6B5AQi(AWPUY*d>;hos|b?Y6Y23xKKQh> z20;d}XQm8Z25hZnQ-(Eg)^@*fbfWUf4YG|vWl+*9sqdp(x2F%xlYFX>IFDdc=VwY% z79IId+5PPcr;G2*`#g9rnE7qefe+%QiYE=f`g&aO9656TjwPxnk$q literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/search.png b/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/search.png deleted file mode 100644 index 23bf66b759f5dbd8eea47cd1316c5fdb0e2c9202..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 388 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z$hLzpWB=2SsX#%=64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq!<_&89iMb zLo9lyUfS!$94Ns0;Cu+i8 z{}%+OUeN7azj(Hh*$1IN!n1ZBTrg!$xB3m8)|{tLq6L1NCS5XZFMPMc$7F-h*WWzN zN=*xawL83^_38@w;!X0L%9#nF7M;e)Yo4Y=-zrx0-Mwd(Sf6vm1HE1IocwQn zPPycK=h5LWi+8Oue(6ztj`gv}ywgiG-~JR>exuvk+{t9Rr|c@9bK-^ywktLZ)F-D0 gHhZdz-)M`!&lveI>_gskV2Ckzy85}Sb4q9e0JU(RJpcdz diff --git a/runelite-client/src/test/java/net/runelite/client/util/StackFormatterTest.java b/runelite-client/src/test/java/net/runelite/client/util/StackFormatterTest.java index 9c344a909f..c36abcf8b3 100644 --- a/runelite-client/src/test/java/net/runelite/client/util/StackFormatterTest.java +++ b/runelite-client/src/test/java/net/runelite/client/util/StackFormatterTest.java @@ -26,12 +26,39 @@ package net.runelite.client.util; import java.text.NumberFormat; import java.text.ParseException; +import java.util.Locale; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import org.junit.Before; import org.junit.Test; public class StackFormatterTest { + @Before + public void setUp() + { + Locale.setDefault(Locale.ENGLISH); + } + + @Test + public void quantityToRSDecimalStackSize() + { + assertEquals("0", StackFormatter.quantityToRSDecimalStack(0)); + assertEquals("8500", StackFormatter.quantityToRSDecimalStack(8_500)); + assertEquals("10K", StackFormatter.quantityToRSDecimalStack(10_000)); + assertEquals("21,7K", StackFormatter.quantityToRSDecimalStack(21_700)); + assertEquals("100K", StackFormatter.quantityToRSDecimalStack(100_000)); + assertEquals("100,3K", StackFormatter.quantityToRSDecimalStack(100_300)); + assertEquals("1M", StackFormatter.quantityToRSDecimalStack(1_000_000)); + assertEquals("8,4M", StackFormatter.quantityToRSDecimalStack(8_450_000)); + assertEquals("10M", StackFormatter.quantityToRSDecimalStack(10_000_000)); + assertEquals("12,8M", StackFormatter.quantityToRSDecimalStack(12_800_000)); + assertEquals("100M", StackFormatter.quantityToRSDecimalStack(100_000_000)); + assertEquals("250,1M", StackFormatter.quantityToRSDecimalStack(250_100_000)); + assertEquals("1B", StackFormatter.quantityToRSDecimalStack(1_000_000_000)); + assertEquals("1,5B", StackFormatter.quantityToRSDecimalStack(1500_000_000)); + assertEquals("2,1B", StackFormatter.quantityToRSDecimalStack(Integer.MAX_VALUE)); + } @Test public void quantityToRSStackSize() From d0f708e26a3329953180669fc368d4e1dc35b471 Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Sun, 22 Apr 2018 04:39:05 +0100 Subject: [PATCH 04/15] Hiscore Plugin redesign Overall: - Applied new colors, positions and sizes, following issue #1342. Search: - Applied the new IconTextField, with search, error and loading indicator (and respective image files). - Blocked tabs witching while results are loading. Endpoints: - Moved the endpoints to right below the search bar (this follows a more logical sequence of usage). - Changed the endpoint presentation style and size. The selected endpoint now displays a orange underline. - Edited the endpoint icons to better fit the visual context. - Changed the way currently selected endpoint is stored. Stats: - Changed the sizing of the labels/panels. - Changed the font to a smaller version. Total/Combat: - Switched the order of the combat and total indicator - Changed the font to a smaller version. Clues/Minigames: - Changed the font to a smaller version. Details Panel: - Completely removed the details panel, instead went for a more in-line with the game approach, tooltips! - Rewrote the way skills and labels are matched - Added html progress bar to the next level --- .../client/plugins/hiscore/HiscorePanel.java | 687 +++++++++--------- .../client/ui/components/IconTextField.java | 11 + .../net/runelite/client/util/SwingUtil.java | 1 + .../plugins/hiscore/hardcore_ironman.png | Bin 280 -> 15224 bytes .../client/plugins/hiscore/ironman.png | Bin 307 -> 15218 bytes .../client/plugins/hiscore/normal.png | Bin 391 -> 16678 bytes .../client/plugins/hiscore/search.png | Bin 388 -> 0 bytes .../plugins/hiscore/ultimate_ironman.png | Bin 365 -> 14668 bytes 8 files changed, 374 insertions(+), 325 deletions(-) delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/search.png 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 3bf64d811c..95ef5b8b41 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 @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Adam + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,10 +26,8 @@ package net.runelite.client.plugins.hiscore; import com.google.common.base.Strings; -import java.awt.BorderLayout; -import java.awt.Color; +import com.google.common.collect.ImmutableList; import java.awt.Dimension; -import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; @@ -39,29 +38,22 @@ import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; import javax.imageio.ImageIO; import javax.inject.Inject; -import javax.swing.BorderFactory; -import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JTextArea; -import javax.swing.JToggleButton; -import javax.swing.UIManager; -import javax.swing.border.Border; -import javax.swing.event.MouseInputAdapter; +import javax.swing.border.EmptyBorder; +import javax.swing.border.MatteBorder; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.Experience; import net.runelite.api.Player; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.components.IconTextField; import net.runelite.client.util.StackFormatter; @@ -102,13 +94,17 @@ import net.runelite.http.api.hiscore.Skill; @Slf4j public class HiscorePanel extends PluginPanel { - private static final String SKILL_NAME = "SKILL_NAME"; - private static final String SKILL = "SKILL"; + /* The maximum allowed username length in runescape accounts */ + private static final int MAX_USERNAME_LENGTH = 12; + + private static final ImageIcon SEARCH_ICON; + private static final ImageIcon LOADING_ICON; + private static final ImageIcon ERROR_ICON; /** * Real skills, ordered in the way they should be displayed in the panel. */ - private static final Set SKILLS = new LinkedHashSet<>(Arrays.asList( + private static final List SKILLS = ImmutableList.of( ATTACK, HITPOINTS, MINING, STRENGTH, AGILITY, SMITHING, DEFENCE, HERBLORE, FISHING, @@ -117,7 +113,7 @@ public class HiscorePanel extends PluginPanel MAGIC, FLETCHING, WOODCUTTING, RUNECRAFT, SLAYER, FARMING, CONSTRUCTION, HUNTER - )); + ); @Inject ScheduledExecutorService executor; @@ -132,24 +128,45 @@ public class HiscorePanel extends PluginPanel private final List skillLabels = new ArrayList<>(); private final JPanel statsPanel = new JPanel(); - private final ButtonGroup endpointButtonGroup = new ButtonGroup(); - private final JTextArea details = new JTextArea(); - private final JProgressBar progressBar; - private List endpointButtons; + /* A list of all the selectable endpoints (ironman, deadman, etc) */ + private final List endPoints = new ArrayList<>(); private final HiscoreClient hiscoreClient = new HiscoreClient(); + private HiscoreResult result; + /* The currently selected endpoint */ + private HiscoreEndpoint selectedEndPoint; + + /* Used to prevent users from switching endpoint tabs while the results are loading */ + private boolean loading = false; + + static + { + try + { + synchronized (ImageIO.class) + { + SEARCH_ICON = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search.png"))); + LOADING_ICON = new ImageIcon(IconTextField.class.getResource("loading_spinner_darker.gif")); + ERROR_ICON = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("error.png"))); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + @Inject public HiscorePanel(HiscoreConfig config) { super(); this.config = config; - // Panel "constants" - // This was an EtchedBorder, but the style would change when the window was maximized. - Border subPanelBorder = BorderFactory.createLineBorder(this.getBackground().brighter(), 2); + setBorder(new EmptyBorder(10, 10, 0, 10)); + setBackground(ColorScheme.DARK_GRAY_COLOR); // Create GBL to arrange sub items GridBagLayout gridBag = new GridBagLayout(); @@ -160,28 +177,11 @@ public class HiscorePanel extends PluginPanel c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.NORTH; - // Search box - JPanel inputPanel = new JPanel(); - inputPanel.setLayout(new BorderLayout(7, 7)); - inputPanel.setBorder(subPanelBorder); - - ImageIcon search; - try - { - BufferedImage icon; - synchronized (ImageIO.class) - { - icon = ImageIO.read(HiscorePanel.class.getResourceAsStream("search.png")); - } - search = new ImageIcon(icon); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - input = new IconTextField(); - input.setIcon(search); + input.setPreferredSize(new Dimension(100, 30)); + input.setBackground(ColorScheme.DARKER_GRAY_COLOR); + input.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); + input.setIcon(SEARCH_ICON); input.addActionListener(e -> executor.execute(this::lookup)); input.addMouseListener(new MouseAdapter() { @@ -192,7 +192,6 @@ public class HiscorePanel extends PluginPanel { return; } - if (client == null) { return; @@ -206,97 +205,20 @@ public class HiscorePanel extends PluginPanel } } }); - inputPanel.add(input, BorderLayout.CENTER); c.gridx = 0; c.gridy = 0; c.weightx = 1; c.weighty = 0; - c.insets = new Insets(0, 0, 3, 0); - gridBag.setConstraints(inputPanel, c); - add(inputPanel); - - // Panel that holds skill icons - GridLayout stats = new GridLayout(8, 3); - statsPanel.setLayout(stats); - statsPanel.setBorder(subPanelBorder); - - // For each skill on the ingame skill panel, create a Label and add it to the UI - for (HiscoreSkill skill : SKILLS) - { - JPanel panel = makeSkillPanel(skill.getName(), skill); - statsPanel.add(panel); - } - - c.gridx = 0; - c.gridy = 1; - gridBag.setConstraints(statsPanel, c); - add(statsPanel); - - JPanel totalPanel = new JPanel(); - totalPanel.setBorder(subPanelBorder); - totalPanel.setLayout(new GridLayout(1, 2)); - - totalPanel.add(makeSkillPanel(OVERALL.getName(), OVERALL)); - totalPanel.add(makeSkillPanel("Combat", null)); - - c.gridx = 0; - c.gridy = 2; - gridBag.setConstraints(totalPanel, c); - add(totalPanel); - - JPanel minigamePanel = new JPanel(); - minigamePanel.setBorder(subPanelBorder); - // These aren't all on one row because when there's a label with four or more digits it causes the details - // panel to change its size for some reason... - minigamePanel.setLayout(new GridLayout(2, 3)); - - minigamePanel.add(makeSkillPanel(CLUE_SCROLL_ALL.getName(), CLUE_SCROLL_ALL)); - minigamePanel.add(makeSkillPanel(LAST_MAN_STANDING.getName(), LAST_MAN_STANDING)); - minigamePanel.add(makeSkillPanel(BOUNTY_HUNTER_ROGUE.getName(), BOUNTY_HUNTER_ROGUE)); - minigamePanel.add(makeSkillPanel(BOUNTY_HUNTER_HUNTER.getName(), BOUNTY_HUNTER_HUNTER)); - - c.gridx = 0; - c.gridy = 3; - gridBag.setConstraints(minigamePanel, c); - add(minigamePanel); - - JPanel detailsPanel = new JPanel(); - detailsPanel.setBorder(subPanelBorder); - detailsPanel.setLayout(new BorderLayout()); - - // Rather than using one JLabel for each line, make a JTextArea look and act like a JLabel - details.setEditable(false); - details.setCursor(null); - details.setOpaque(false); - details.setFocusable(false); - details.setWrapStyleWord(true); - details.setLineWrap(true); - details.setMargin(new Insets(2, 4, 4, 4)); - details.setRows(6); - details.setText(""); - - detailsPanel.add(details, BorderLayout.CENTER); - - progressBar = new JProgressBar(); - progressBar.setStringPainted(true); - progressBar.setValue(0); - progressBar.setMinimum(0); - progressBar.setMaximum(100); - progressBar.setBackground(Color.RED); - progressBar.setVisible(false); - - detailsPanel.add(progressBar, BorderLayout.SOUTH); - - c.gridx = 0; - c.gridy = 4; - gridBag.setConstraints(detailsPanel, c); - add(detailsPanel); + c.insets = new Insets(0, 0, 10, 0); + gridBag.setConstraints(input, c); + add(input); + /* The container for all the endpoint selectors */ JPanel endpointPanel = new JPanel(); - endpointPanel.setBorder(subPanelBorder); + endpointPanel.setOpaque(false); + endpointPanel.setLayout(new GridLayout(1, 5, 7, 1)); - endpointButtons = new ArrayList<>(); for (HiscoreEndpoint endpoint : HiscoreEndpoint.values()) { try @@ -307,25 +229,32 @@ public class HiscorePanel extends PluginPanel iconImage = ImageIO.read(HiscorePanel.class.getResourceAsStream( endpoint.name().toLowerCase() + ".png")); } - JToggleButton button = new JToggleButton(); - button.setIcon(new ImageIcon(iconImage)); - button.setPreferredSize(new Dimension(24, 24)); - button.setBackground(Color.WHITE); - button.setFocusPainted(false); - button.setActionCommand(endpoint.name()); - button.setToolTipText(endpoint.getName() + " Hiscores"); - button.addActionListener((e -> executor.execute(this::lookup))); - button.addMouseListener(new MouseAdapter() + + JPanel panel = new JPanel(); + JLabel label = new JLabel(); + + label.setIcon(new ImageIcon(iconImage)); + + panel.add(label); + panel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + panel.setToolTipText(endpoint.getName() + " Hiscores"); + panel.addMouseListener(new MouseAdapter() { @Override - public void mouseReleased(MouseEvent e) + public void mouseClicked(MouseEvent e) { + if (loading) + { + return; + } + executor.execute(HiscorePanel.this::lookup); + selectedEndPoint = endpoint; updateButtons(); } }); - endpointButtons.add(button); - endpointButtonGroup.add(button); - endpointPanel.add(button); + + endPoints.add(panel); + endpointPanel.add(panel); } catch (IOException ex) { @@ -333,156 +262,61 @@ public class HiscorePanel extends PluginPanel } } - endpointButtons.get(0).setSelected(true); - endpointButtons.get(0).setBackground(Color.CYAN); + /* Default endpoint is the general (normal) endpoint */ + selectedEndPoint = HiscoreEndpoint.NORMAL; + updateButtons(); c.gridx = 0; - c.gridy = 5; - // Last item has a nonzero weighty so it will expand to fill vertical space - c.weighty = 1; + c.gridy = 1; gridBag.setConstraints(endpointPanel, c); add(endpointPanel); - } - void addInputKeyListener(KeyListener l) - { - this.input.addKeyListener(l); - } + // Panel that holds skill icons + GridLayout stats = new GridLayout(8, 3); + statsPanel.setLayout(stats); + statsPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + statsPanel.setBorder(new EmptyBorder(5, 0, 5, 0)); - void removeInputKeyListener(KeyListener l) - { - this.input.removeKeyListener(l); - } - - private void changeDetail(String skillName, HiscoreSkill skill) - { - if (result == null || result.getPlayer() == null) + // For each skill on the ingame skill panel, create a Label and add it to the UI + for (HiscoreSkill skill : SKILLS) { - return; + JPanel panel = makeSkillPanel(skill); + panel.setOpaque(false); + statsPanel.add(panel); } - String text; - int progress = -1; - switch (skillName) - { - case "Combat": - { - double combatLevel = Experience.getCombatLevelPrecise( - result.getAttack().getLevel(), - result.getStrength().getLevel(), - result.getDefence().getLevel(), - result.getHitpoints().getLevel(), - result.getMagic().getLevel(), - result.getRanged().getLevel(), - result.getPrayer().getLevel() - ); - text = "Skill: Combat" + System.lineSeparator() - + "Exact Combat Level: " + StackFormatter.formatNumber(combatLevel) + System.lineSeparator() - + "Experience: " + StackFormatter.formatNumber(result.getAttack().getExperience() - + result.getStrength().getExperience() + result.getDefence().getExperience() - + result.getHitpoints().getExperience() + result.getMagic().getExperience() - + result.getRanged().getExperience() + result.getPrayer().getExperience()); - break; - } - case "Clue Scrolls (all)": - { - String allRank = (result.getClueScrollAll().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollAll().getRank()); - String easyRank = (result.getClueScrollEasy().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollEasy().getRank()); - String mediumRank = (result.getClueScrollMedium().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollMedium().getRank()); - String hardRank = (result.getClueScrollHard().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollHard().getRank()); - String eliteRank = (result.getClueScrollElite().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollElite().getRank()); - String masterRank = (result.getClueScrollMaster().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollMaster().getRank()); - String all = (result.getClueScrollAll().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollAll().getLevel())); - String easy = (result.getClueScrollEasy().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollEasy().getLevel())); - String medium = (result.getClueScrollMedium().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollMedium().getLevel())); - String hard = (result.getClueScrollHard().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollHard().getLevel())); - String elite = (result.getClueScrollElite().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollElite().getLevel())); - String master = (result.getClueScrollMaster().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollMaster().getLevel())); - text = "All clues: " + all + " | Rank: " + allRank + System.lineSeparator() - + "Easy: " + easy + " | Rank: " + easyRank + System.lineSeparator() - + "Medium: " + medium + " | Rank: " + mediumRank + System.lineSeparator() - + "Hard: " + hard + " | Rank: " + hardRank + System.lineSeparator() - + "Elite: " + elite + " | Rank: " + eliteRank + System.lineSeparator() - + "Master: " + master + " | Rank: " + masterRank; - break; - } - case "Bounty Hunter - Rogue": - { - String rank = (result.getBountyHunterRogue().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getBountyHunterRogue().getRank()); - text = "Bounty Hunter - Rogue Kills" + System.lineSeparator() - + "Rank: " + rank; - break; - } - case "Bounty Hunter - Hunter": - { - String rank = (result.getBountyHunterHunter().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getBountyHunterHunter().getRank()); - text = "Bounty Hunter - Hunter Kills" + System.lineSeparator() - + "Rank: " + rank; - break; - } - case "Last Man Standing": - { - String rank = (result.getLastManStanding().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getLastManStanding().getRank()); - text = "Last Man Standing" + System.lineSeparator() - + "Rank: " + rank; - break; - } - case "Overall": - { - Skill requestedSkill = result.getSkill(skill); - String rank = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getRank()); - String exp = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getExperience()); - text = "Skill: " + skillName + System.lineSeparator() - + "Rank: " + rank + System.lineSeparator() - + "Experience: " + exp; - break; - } - default: - { - Skill requestedSkill = result.getSkill(skill); - String rank = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getRank()); - String exp = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getExperience()); - String remainingXp; - if (requestedSkill.getRank() == -1) - { - remainingXp = "Unranked"; - } - else - { - int currentLevel = Experience.getLevelForXp((int) requestedSkill.getExperience()); - int currentXp = (int) requestedSkill.getExperience(); - int xpForCurrentLevel = Experience.getXpForLevel(currentLevel); - int xpForNextLevel = currentLevel + 1 <= Experience.MAX_VIRT_LEVEL ? Experience.getXpForLevel(currentLevel + 1) : -1; + c.gridx = 0; + c.gridy = 2; + gridBag.setConstraints(statsPanel, c); + add(statsPanel); - remainingXp = xpForNextLevel != -1 ? StackFormatter.formatNumber(xpForNextLevel - currentXp) : "0"; + JPanel totalPanel = new JPanel(); + totalPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + totalPanel.setLayout(new GridLayout(1, 2)); - double xpGained = currentXp - xpForCurrentLevel; - double xpGoal = xpForNextLevel != -1 ? xpForNextLevel - xpForCurrentLevel : 100; - progress = (int) ((xpGained / xpGoal) * 100f); + totalPanel.add(makeSkillPanel(null)); //combat has no hiscore skill, refered to as null + totalPanel.add(makeSkillPanel(OVERALL)); - } - text = "Skill: " + skillName + System.lineSeparator() - + "Rank: " + rank + System.lineSeparator() - + "Experience: " + exp + System.lineSeparator() - + "Remaining XP: " + remainingXp; - break; - } - } + c.gridx = 0; + c.gridy = 3; + gridBag.setConstraints(totalPanel, c); + add(totalPanel); - details.setFont(UIManager.getFont("Label.font")); - details.setText(text); + JPanel minigamePanel = new JPanel(); + // These aren't all on one row because when there's a label with four or more digits it causes the details + // panel to change its size for some reason... + minigamePanel.setLayout(new GridLayout(2, 3)); + minigamePanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); - if (progress >= 0) - { - progressBar.setVisible(true); - progressBar.setValue(progress); - progressBar.setBackground(Color.getHSBColor((progress / 100.f) * (120.f / 360.f), 1, 1)); - } - else - { - progressBar.setVisible(false); - } + minigamePanel.add(makeSkillPanel(CLUE_SCROLL_ALL)); + minigamePanel.add(makeSkillPanel(LAST_MAN_STANDING)); + minigamePanel.add(makeSkillPanel(BOUNTY_HUNTER_ROGUE)); + minigamePanel.add(makeSkillPanel(BOUNTY_HUNTER_HUNTER)); + c.gridx = 0; + c.gridy = 4; + gridBag.setConstraints(minigamePanel, c); + add(minigamePanel); } @Override @@ -492,16 +326,14 @@ public class HiscorePanel extends PluginPanel input.requestFocusInWindow(); } - private JPanel makeSkillPanel(String skillName, HiscoreSkill skill) + /* Builds a JPanel displaying an icon and level/number associated with it */ + private JPanel makeSkillPanel(HiscoreSkill skill) { JLabel label = new JLabel(); + label.setFont(FontManager.getRunescapeSmallFont()); label.setText("--"); - // Store the skill that the label displays so we can tell them apart - label.putClientProperty(SKILL_NAME, skillName); - label.putClientProperty(SKILL, skill); - - String skillIcon = "skill_icons_small/" + skillName.toLowerCase() + ".png"; + String skillIcon = "skill_icons_small/" + (skill == null ? "combat" : skill.getName().toLowerCase()) + ".png"; log.debug("Loading skill icon from {}", skillIcon); try @@ -518,36 +350,31 @@ public class HiscorePanel extends PluginPanel log.warn(null, ex); } - // Show skill details on hover - label.addMouseListener(new MouseInputAdapter() - { - @Override - public void mouseEntered(MouseEvent e) - { - JLabel source = (JLabel) e.getSource(); - String skillName = (String) source.getClientProperty(SKILL_NAME); - HiscoreSkill skill = (HiscoreSkill) label.getClientProperty(SKILL); - changeDetail(skillName, skill); - } - }); - skillLabels.add(label); + boolean totalLabel = skill == HiscoreSkill.OVERALL || skill == null; //overall or combat + label.setIconTextGap(totalLabel ? 10 : 4); JPanel skillPanel = new JPanel(); + skillPanel.setOpaque(false); + skillPanel.setBorder(new EmptyBorder(2, 0, 2, 0)); + skillLabels.add(label); skillPanel.add(skillLabels.get(skillLabels.size() - 1)); + return skillPanel; } public void lookup(String username) { input.setText(username); + + selectedEndPoint = HiscoreEndpoint.NORMAL; //reset the endpoint to regular player + updateButtons(); + lookup(); } private void lookup() { String lookup = input.getText(); - details.setText("Loading..."); - progressBar.setVisible(false); lookup = sanitize(lookup); @@ -556,32 +383,66 @@ public class HiscorePanel extends PluginPanel return; } + /* Runescape usernames can't be longer than 12 characters long */ + if (lookup.length() > MAX_USERNAME_LENGTH) + { + input.setIcon(ERROR_ICON); + loading = false; + return; + } + + input.setEditable(false); + input.setIcon(LOADING_ICON); + loading = true; + for (JLabel label : skillLabels) { label.setText("--"); } + // if for some reason no endpoint was selected, default to normal + if (selectedEndPoint == null) + { + selectedEndPoint = HiscoreEndpoint.NORMAL; + } + try { - HiscoreEndpoint endpoint = HiscoreEndpoint.valueOf(endpointButtonGroup.getSelection().getActionCommand()); - log.debug("Hiscore endpoint " + endpoint.name() + " selected"); - - result = hiscoreClient.lookup(lookup, endpoint); + log.debug("Hiscore endpoint " + selectedEndPoint.name() + " selected"); + result = hiscoreClient.lookup(lookup, selectedEndPoint); } catch (IOException ex) { log.warn("Error fetching Hiscore data " + ex.getMessage()); - details.setText("Error fetching Hiscore data"); - progressBar.setVisible(false); + input.setIcon(ERROR_ICON); + input.setEditable(true); + loading = false; return; } + /* + For some reason, the fetch results would sometimes return a not null object + with all null attributes, to check for that, i'll just null check one of the attributes. + */ + if (result.getAttack() == null) + { + input.setIcon(ERROR_ICON); + input.setEditable(true); + loading = false; + return; + } + + //successful player search + input.setIcon(SEARCH_ICON); + input.setEditable(true); + loading = false; + + int index = 0; for (JLabel label : skillLabels) { - String skillName = (String) label.getClientProperty(SKILL_NAME); - HiscoreSkill skill = (HiscoreSkill) label.getClientProperty(SKILL); + HiscoreSkill skill = find(index); - if (skillName.equals("Combat")) + if (skill == null) { if (result.getPlayer() != null) { @@ -600,7 +461,6 @@ public class HiscorePanel extends PluginPanel else if (result.getSkill(skill) != null && result.getSkill(skill).getRank() != -1) { Skill s = result.getSkill(skill); - int level; if (config.virtualLevels() && SKILLS.contains(skill)) { @@ -613,12 +473,191 @@ public class HiscorePanel extends PluginPanel label.setText(Integer.toString(level)); } + + label.setToolTipText(detailsHtml(skill)); + index++; + } + } + + void addInputKeyListener(KeyListener l) + { + this.input.addInputKeyListener(l); + } + + void removeInputKeyListener(KeyListener l) + { + this.input.removeInputKeyListener(l); + } + + /* + Returns a hiscore skill based on it's display order. + */ + private HiscoreSkill find(int index) + { + if (index < SKILLS.size()) + { + return SKILLS.get(index); } - // Clear details panel - details.setFont(UIManager.getFont("Label.font").deriveFont(Font.ITALIC)); - details.setText("Hover over a skill for details"); - progressBar.setVisible(false); + switch (index - SKILLS.size()) + { + case 0: + return null; + case 1: + return HiscoreSkill.OVERALL; + case 2: + return HiscoreSkill.CLUE_SCROLL_ALL; + case 3: + return HiscoreSkill.LAST_MAN_STANDING; + case 4: + return HiscoreSkill.BOUNTY_HUNTER_ROGUE; + case 5: + return HiscoreSkill.BOUNTY_HUNTER_HUNTER; + } + + return null; + } + + /* + Builds a html string to display on tooltip (when hovering a skill). + */ + private String detailsHtml(HiscoreSkill skill) + { + String openingTags = ""; + String closingTags = ""; + + String content = ""; + + if (result != null) + { + if (skill == null) + { + double combatLevel = Experience.getCombatLevelPrecise( + result.getAttack().getLevel(), + result.getStrength().getLevel(), + result.getDefence().getLevel(), + result.getHitpoints().getLevel(), + result.getMagic().getLevel(), + result.getRanged().getLevel(), + result.getPrayer().getLevel() + ); + + double combatExperience = result.getAttack().getExperience() + + result.getStrength().getExperience() + result.getDefence().getExperience() + + result.getHitpoints().getExperience() + result.getMagic().getExperience() + + result.getRanged().getExperience() + result.getPrayer().getExperience(); + + content += "

Skill: Combat

"; + content += "

Exact Combat Level: " + StackFormatter.formatNumber(combatLevel) + "

"; + content += "

Experience: " + StackFormatter.formatNumber(combatExperience) + "

"; + } + else + { + switch (skill) + { + case CLUE_SCROLL_ALL: + { + String rank = (result.getClueScrollAll().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollAll().getRank()); + String allRank = (result.getClueScrollAll().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollAll().getRank()); + String easyRank = (result.getClueScrollEasy().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollEasy().getRank()); + String mediumRank = (result.getClueScrollMedium().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollMedium().getRank()); + String hardRank = (result.getClueScrollHard().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollHard().getRank()); + String eliteRank = (result.getClueScrollElite().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollElite().getRank()); + String masterRank = (result.getClueScrollMaster().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getClueScrollMaster().getRank()); + String all = (result.getClueScrollAll().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollAll().getLevel())); + String easy = (result.getClueScrollEasy().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollEasy().getLevel())); + String medium = (result.getClueScrollMedium().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollMedium().getLevel())); + String hard = (result.getClueScrollHard().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollHard().getLevel())); + String elite = (result.getClueScrollElite().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollElite().getLevel())); + String master = (result.getClueScrollMaster().getLevel() == -1 ? "0" : StackFormatter.formatNumber(result.getClueScrollMaster().getLevel())); + content += "

All: " + all + " Rank: " + allRank + "

"; + content += "

Easy: " + easy + " Rank: " + easyRank + "

"; + content += "

Medium: " + medium + " Rank: " + mediumRank + "

"; + content += "

Hard: " + hard + " Rank: " + hardRank + "

"; + content += "

Elite: " + elite + " Rank: " + eliteRank + "

"; + content += "

Master: " + master + " Rank: " + masterRank + "

"; + break; + } + case BOUNTY_HUNTER_ROGUE: + { + String rank = (result.getBountyHunterRogue().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getBountyHunterRogue().getRank()); + content += "

Rank: " + rank + "

"; + break; + } + case BOUNTY_HUNTER_HUNTER: + { + String rank = (result.getBountyHunterHunter().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getBountyHunterHunter().getRank()); + content += "

Rank: " + rank + "

"; + break; + } + case LAST_MAN_STANDING: + { + String rank = (result.getLastManStanding().getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(result.getLastManStanding().getRank()); + content += "

Rank: " + rank + "

"; + break; + } + case OVERALL: + { + Skill requestedSkill = result.getSkill(skill); + String rank = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getRank()); + String exp = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getExperience()); + content += "

Skill: " + skill.getName() + "

"; + content += "

Rank: " + rank + "

"; + content += "

Experience: " + exp + "

"; + break; + } + default: + { + Skill requestedSkill = result.getSkill(skill); + + String rank = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getRank()); + String exp = (requestedSkill.getRank() == -1) ? "Unranked" : StackFormatter.formatNumber(requestedSkill.getExperience()); + String remainingXp; + if (requestedSkill.getRank() == -1) + { + remainingXp = "Unranked"; + } + else + { + int currentLevel = Experience.getLevelForXp((int) requestedSkill.getExperience()); + remainingXp = (currentLevel + 1 <= Experience.MAX_VIRT_LEVEL) ? StackFormatter.formatNumber(Experience.getXpForLevel(currentLevel + 1) - requestedSkill.getExperience()) : "0"; + } + + content += "

Skill: " + skill.getName() + "

"; + content += "

Rank: " + rank + "

"; + content += "

Experience: " + exp + "

"; + content += "

Remaining XP: " + remainingXp + "

"; + + break; + } + } + } + } + + /** + * Adds a html progress bar to the hover information + */ + if (SKILLS.contains(skill)) + { + int currentLevel = Experience.getLevelForXp((int) result.getSkill(skill).getExperience()); + int currentXp = (int) result.getSkill(skill).getExperience(); + int xpForCurrentLevel = Experience.getXpForLevel(currentLevel); + int xpForNextLevel = currentLevel + 1 <= Experience.MAX_VIRT_LEVEL ? Experience.getXpForLevel(currentLevel + 1) : -1; + + double xpGained = currentXp - xpForCurrentLevel; + double xpGoal = xpForNextLevel != -1 ? xpForNextLevel - xpForCurrentLevel : 100; + int progress = (int) ((xpGained / xpGoal) * 100f); + + // had to wrap the bar with an empty div, if i added the margin directly to the bar, it would mess up + content += "
" + + "
" + + "
" + + "
" + + "
" + + "
"; + } + + return openingTags + content + closingTags; } private static String sanitize(String lookup) @@ -626,20 +665,18 @@ public class HiscorePanel extends PluginPanel return lookup.replace('\u00A0', ' '); } + /* + When an endpoint gets selected, this method will correctly display the selected one + with an orange underline. + */ private void updateButtons() { - for (JToggleButton button : endpointButtons) + for (JPanel panel : endPoints) { - Color color; - if (button.isSelected()) - { - color = Color.CYAN; - } - else - { - color = Color.WHITE; - } - button.setBackground(color); + panel.setBorder(new EmptyBorder(0, 0, 1, 0)); } + + int selectedIndex = selectedEndPoint.ordinal(); + endPoints.get(selectedIndex).setBorder(new MatteBorder(0, 0, 1, 0, ColorScheme.BRAND_ORANGE)); } -} +} \ 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 index fde8d673cb..e016215756 100644 --- 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 @@ -30,6 +30,7 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionListener; +import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.ImageIcon; @@ -129,6 +130,16 @@ public class IconTextField extends JPanel this.backgroundColor = color; } + public void addInputKeyListener(KeyListener l) + { + textField.addKeyListener(l); + } + + public void removeInputKeyListener(KeyListener l) + { + textField.removeKeyListener(l); + } + public void setHoverBackgroundColor(Color hoverBackgroundColor) { if (hoverBackgroundColor == null) 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 674ebe07dd..41ddd1b9fb 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 @@ -79,6 +79,7 @@ public class SwingUtil // Force heavy-weight popups/tooltips. // Prevents them from being obscured by the game applet. ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false); + ToolTipManager.sharedInstance().setInitialDelay(300); JPopupMenu.setDefaultLightWeightPopupEnabled(false); UIManager.put("Button.foreground", Color.WHITE); diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/hardcore_ironman.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/hardcore_ironman.png index 2f4631d36ea0ebadf0394ece5a54d85e5ad5a755..bf70440802fbb5fb6f392335c26a7cafcdf04509 100644 GIT binary patch literal 15224 zcmeI3Yitx%6vqc*2v#wKhY(}JvZ#UJ>^!%-JK5c)bPH`@ODUDM#30j|yY1NBnQ>;? z-PQz)V#F^^K_Ws>Oe9(od8DF=BEgVIsu+zik!Um~icd_4ny4fK-r48$c6-w(#xHl1 zeVqHh=bSsgd(YYVGEcX*teaPJR}Do`^O~CyZSZ@6`<^=oes1}2qyv84YBqJ*6m{D| z_dSa`cxWj_MSj-WJDtwt21zk80a-P=K_Hhg;cSYE*5yoD=>v|h8}w*;%zx$dS-($H zWB!g{l1-WopjT@evcTq{mUd;RPl>4hx_C`AC&2(2;K;sQCav32F6Ph2mEdo8nDP7a zQ=Gn-zuryg>rA%#8Vn2gf`M9EVQWLaP$a+wLm@GAzmI3RAj1lbz|y=RiL4~@zT(Rt zuYtd!mYR~<5^Ia;pcV7?I*utZOg5VhWQBlX^)Or{5@A@L;dvU)pzT53k#n?euP6j5 z#Yq5Lu{6`s4Bh9(mAj38C+7FNiOR3yb!E(QBHb>sgA|#ZY%*McWvV%;O4-Nkx6=8g zsR{$qAOm#AhJIXCKeN|x47=BuCS_TkHZk0jWU`#Q>UuMos;O}>pd3IOE1WGU%CEK2=hKkv1&2ED#r>OtoQ;SN^0)&ARQ#x&oRLaX1jrG*yyo1)dMGEG>pu zj*iHpLWc#Fqf@*fh%B#waCZS>GU;`n5;|ch{cg3u(1pgL8VU?wSr)5`iYf{UEdW)d zISz#BupAE2YDkSl!l6it5Qxa{oG}!m#v}cZcEhC0TKMTU>F=1E^7E(r9H_WzV9UHg!8^hM*AN<34?kY%kDpV4oX0~&M{hVA`rD~m8iBb2@NrF`jPn4Xm9@ssmj8!rh=6AZ)nC{Qd z5AiH~F`WBZboI)pYZu{Al;eJU>~;Ke!_)|KB!RDv1>XEMJ3#QFzk_?>Cw94d;~dEO)V+ z`J-}0H!~^{izGpS;7x)H&j(QnE(8ePB)IT=5S8FUfZ$Dn3(p5p2`&T(-Xys2d=Qo3 zLV(~+f(y?FQ3);t2;L;P@O%)J;6i}lO@a&02T=(w1PIcvi$RYXM_&X=Q?!ErU;ksv@n?IX6zjRS<`^d=R*0no+Uvjli z>Ze}$c&y{pSl=-Jf{?st?}{V4=XCwDclV-S-`=^w{_V_{TgFeG{FMIbtp&2MZ_~yj zC*Qc3iXR9Mf2(J+dz#OGm0Vv#{q^99Gxq9d*X=mC>DEhIZ?s)HaP-=e9ZeH;-+cFl z_`!K)f8hA-k8bO{ERNr4N|9Bk9`1Ok@$&59^?w38JaG8E=U)nUfA;>+*%NJhHa>MM zaOAPkcVAqXS-yY#nEi?Kk=CcZynL5szP9Yz#H9-pXXjB3w`BRdj}OM&XJeZiTN20D HY=8W3M!}u^ delta 233 zcmexSHiKz`3O`$tx4R1i82ohJT|ZG#yq;l)r;B5V#p&dP1wsibOBCYc{@F7#Gas&2 zQxo~<`u+Xl7pVuAOR`A%FV) zeT}y^PT-W%EkcWvMI h=P!418% diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/ironman.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/ironman.png index a00b92b1f864da7029027c53fe2fe158517e2f3b..453e0326de4b2f269e33ac78bc5abee5deb18b9c 100644 GIT binary patch literal 15218 zcmeI3Ym5_B6vu}UL6;DD#6XC~NeKpl={(wQJ7Zg1T3|O)*z8t!7c@AXx!q2-ovAak zw7bcI0o14j#F(JGR0u{BvkDS45aLEnOi(eA4;V?5$QPrK6;O~5M(^}Fd$)VDQH)=1 zlRnP<-*e8~-@WH_zRdIOYgf-~TF^vM)XcWlcnAER^-IZ_cu_$WB zT<1N7+Pi-~MTwWSWVhX&SR*M$&MT`%3V4gTJe*BY;pN4=tn>k!PJv!ck9bB;ee9t% zHR9VL3=O zP8?W@spW0W&}k>GoHF|Dh{xk3s=X@f%H?Z`bgRM+Qe=yAp5?t9+sH{(YCidXGh4be zRbfFE}8Bx<=EUT-efFtuf`+yoI+kT&$Rk^_0b zb^y!hHx;mQ6I{x|Myt2AbbWKiL~@h~uN@S%3EUi|>{dDo*Z zpR)EULOK>Joho{9m!_*m!Fn*vqAvCQu15oL*#=Q~9zzW0XShI;7bQL<`T3j_BVnjodSFZp-k0P8k}oLnoI8eVCK?0Qk1E^pq@uXzy5gvcls3$qY)7@6+zZ&e z-Wz7?gRYsiQzgZWtYN~-0#RR>Z8YriDxEZ`O}A`WS3p}l3J1KJrb>!G1q3ziXH?P0 zGeXesXXLcVF#%bTLwqP8C_cUfF_Cb)j|&|)lzyjLVCZsVQ4Ix#uV0owo#q40qR9Aw z>SuT!gqV;V3NmU?6~$0cO!Go5kt^7^MC(iq_B}c4n&YfRg~>QkNQr7%VS=KnFo5$5 z3~Vqe!!^qSkOesg@>Gzc>;RjX#f(>mZf#qcbbI$I}rYV2rL$ZDkym6uHmIQ19t zR12`y(rO*c@hR1@Br8sDiz-fe05!~x8LeMd&9maBjo;46At=Xpxlf#Yr{ygAVooZA1MO zD;Zhu1!|P79^mT0 zYekUlj%%4W#1Z0&|~TzS^I*zNpL zU(xN1io_yG5FmJy;KK7kRDufuf;R~+JRd|QxDX(CliXil5na}_?^Nh_@#35ptAL{U#~qo`Yd!SA0as^d;-%01l_ zCGDZ8dB*wm_rF9@Q>eChOS1UgyRSrsw{f$!FF$B54#iHk?jJe*(Cb@w9-K=bIeg@q z>$H4kXy(o@({0x_uUc``1@XWEYx=(a zC-zW7KYYEnOTDyZ%_B3YWnY+Y7mhBudVR-&H;!HI+r93s8=c#IZ~odA!CsgF2B@?)|(czT|_${!|Xke^#7#YxMo2LKAx|bmg-% ynQu;{cI>ir=I#ygz32A*`Sa@Zl_euLsbts7H@93k^r`byY}?AU@!=IuZv7hsqMcX( delta 260 zcmV+f0sH>)cC!MIBo78+OGiWi{{a60|De66laV18e*n`-L_t(I%e9le5`!QRgqKX- zD(PZJNp}xuXK6=k9x1|%%%w0mhXTg@?2I#@=W=`t3U%?$cNGWcJp&>F0C?|{g0Ab5 zdCrLA&rxfWoKByZIOleaej+9!CL(?@N*-fO?tPh2+qTQt7?W&AYYhPC`#wo*Gl{E6 zgB%m@YCWvA)%exAWoc4N?_1bhj2{3(m=MBL?WSoOR2fB}1t!I7qK@3H>+LcZxw&i@ z?u*AJe%X=SjE!cc;;Y3f#6mLu;%L;t}0000< KMNUMnLSTZ2rF9_y diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/normal.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/normal.png index c00f4ec040ae8484df41085d428a99f043fe9e4a..375dd4e906602c8266810d555534ad6a20ba7b4c 100644 GIT binary patch literal 16678 zcmeI3e{d969l-bQFr=|i#2+*rX1F<|wzhk_d%yN>y&MyBr^&Pz&Ll`c>|}5EU9!X7 z?Qy%wT|&cj%nTz?N~f`617m)48a*s7HlPzN1UjBoGf z=H>E+D(dLGo%^xh_kG{*ci-oI-}mkQv0G#9UtZ8~TLVQ=3)))49q|7X@Vltq27gao zl2h>SrcCQvjiNsON#kpw#`fGwQNjC_u5P_Mdaod#h zanP%zn;e&Z__D*U$W4wl-Y6T*w17UPbtnruhuXWOp$$?%cB~9F1oHxPkOI1B&!>`U zO~^Mn3VsE6ZZtCvd!dWIp~=x~1hjWYWA+v`3+!H}kCs>;Z|4I}&dc*2{%*UA<-82* zX51|8atj_-@VM-y!x3tLXThwT5IVwNDushmlcP`9GXlfpaye(t?Nqb93>OFl7}mwO zTr})KYlCTB%+qOYdC^JPPZ(%YR>|m!nzkE$#kkt9H#rj42!aDYhob{N4^Q&}zZ8gzE-~(s${}jFnH8#4(O|X?nz9(?a-p(y zf{im<%Zj8@s8AU(s{v#EH95))uu{@070a+GtYblxjM^5GjO793ATy)2dRZ0Al9@7l zIqqN_r$xt(ZgLeGg=(A*0%vswsPM756F$yRMdm!jdiy%+&ioI4VsX_}7w)2Z6X0_qgc* z=N0L=pa`Jxk+XLqxvFbZ>J!twKn^kGk(C?% zmuBwzs(camKRt8i9rOQS=8E-YW~DFh9Vd8m=`N`pkwVMai%@Ri?*;!k$oUg%2U{`G=`I z0L|JiD!b~9JgJW8MtVhjkst^Vyhw22`5;ndEVfgkl*RH{rjqOrvN0g$5R#4OfBNTOY z8vg&5qB`m+%cr|3O4vnFOVqd4Eqj=vY*g&tu89A$w?4ALHk!0Nw$U=S(XvOg?9r{` z{g&~5>-d0mZ@*<k~uPi6QHg@UzK!V3Y0O{kHE7TMrJ;o7mj=?3TsPjaZ-G zQg>vl^~g5si`%V7x7QupVLkp3{Me4|sGl6QO^()`9JQVtwVm2!d*xfVOpV#5cH2&m z&6^tg*wpU2$wwETc(n0H-&ykGvBuZO>&}hOfBn1lZ|t4_=Kh<0d2qpN`)+$}f8)7{ z#y6f^cK+#Q=MFG$KV?7v%!>EEzv$OLSpNRuJ3n~gu8S|y?;dWteBAx^;WZz=6#31G zuU?w$ynLeh%E`#(Q|qt%WZlJAQ-3-w{(egR^I7%E*@5Y42!F3 z^@U$=|HYAIn<5YFa=yz=WOjaLQEcV<;WO{QdGmeERPyMSeaGkhR3G`vTko8|@b{?; zBYUTIuDs`RL(lE>J1=cM`sxSk&b+tt!Rc>);b$Ye4_PwFiQn-LUETH9JhNorE5jF; h#_qVN+`M@{75vqzZIhR-EjLo7Ez%x-=8HYw_y>NU2G9Ti delta 380 zcmV-?0fYXgf&qsEGl@`6M-2)Z3IG5A4M|8uQUCw|5C8xG5C{eU001BJ|6u?C00v@9 zM??Ss00000`9r&Zks%j<0VhdBK~y-)wUa$g!$1&)zwtT|R)}qU1H?%X2cV*)3r7eY z7k~z8(os@zfE)pF0u-e5cH$_CWP3dncC1(^hJsADt9d{3W)}`wLneB6`=MPu&Oz0O zOf)jnP8XdHgb3g7HtBSMT@&DkhPT&Mhe&N#i13a=JWl{f(~@3)Pw+g!4-J=BLjYc$ z7hKgJi3s{8!00004nJ z$hLzpWB=2SsX#%=64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq!<_&89iMb zLo9lyUfS!$94Ns0;Cu+i8 z{}%+OUeN7azj(Hh*$1IN!n1ZBTrg!$xB3m8)|{tLq6L1NCS5XZFMPMc$7F-h*WWzN zN=*xawL83^_38@w;!X0L%9#nF7M;e)Yo4Y=-zrx0-Mwd(Sf6vm1HE1IocwQn zPPycK=h5LWi+8Oue(6ztj`gv}ywgiG-~JR>exuvk+{t9Rr|c@9bK-^ywktLZ)F-D0 gHhZdz-)M`!&lveI>_gskV2Ckzy85}Sb4q9e0JU(RJpcdz diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/ultimate_ironman.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/ultimate_ironman.png index efa7643d971e6026e959c2c47b2f9fd489fd9eb0..07eb519971580328d7f4d059d7a941fdebd785d8 100644 GIT binary patch literal 14668 zcmeI3Ux*t;9LM*-KQ+=mSX!$-bj@L{)Xn}&F3Fmt)=TXjnCo$u+Fbi!H@lO&ZIj(~ zcdyAELe+v$YNajJR$Hx5A4IAz1qBrqd{C4Yl!B-TQWW}7DD8^~>N&fcOMb~Eohp5t zg=Bx3`Tl;tna})YvM+mJd~D0|$o&z9VU~}M&;D zJsaJgC9$-vk^am zTx-_R(B{2(Ducad?-`Z$;&epvd-{$YHHd89Y~I*g>jiIYrB==E5o) zUqr|b4+mAnuHSB$x>a`{$?)AS?e%U)LwVIhIb4q^PKa|tq9~>nF{Q-C4T7i$0tgZ+ zyJ8e9-KflU#1Q48kWi$QBFiuaPNMhQb2`Jf z8-cP_Rm!kcwH!Pwl#?@juV&yCRGKnsx}IukXf&V04Kc&el~lT1mV^Z2#H3u|l6pMO zsj^n)w2G`_sOdzyQVt*%3ebH?=)9%P`nv^(4i6UH(s20pu*y=rT&{>oPDZ-Ki6TmI zDK(Yk^rW6nr;_Q4D2a=SK(Hl=wmJqr_tYxT_;b6U+Ar_OYUB{@Iq({Oy7C4Bye(9#+e<>1O=j z>8>KaASx)*U{>`~L-1qjMp`*3R`= zEUTsKnemS}1=j)|ml~`AuE8a3?aZORYuWcem@*ja^}Pw`<#T zn`wACshO>-8F=~UaCk^e;ztIQ8%iYPM27Ef?)Dfr8mL;z8+bRnepQO8rcZZAPsgc# zj-aFI7%mm~a}QQBY{icb_^E|&J+^c%a%&^pVxv8yTO1gMqe&nQqD^rT5kUdP1!)j% zii?N{3Mei}gJ@G+L_|q$sK>@`DX%KCSi--scC@x5YXj5E7L{LC+K^jDx z;vyn~0*VXLAleic5fKznT#yFQrnrcRpn&3nG>A6EMMMMz6c?mHv?(qkA}FA^APu5T zaS;(g0mTJr5N(Q!hzJTOE=YrDQ(QzuP(X1(8btesxFYQr0g;K{{;T8H{4QQU@DF}P zkkv*e3Jf#<5W_sbpJD!7!1v1xGqH>rSXE*e#Nu9T*>b~@>)dR|N0A$53HGrFo!m-zj)-nzfP_G{^e7vj_%0k zpF91{m0$mOU?Fn!!nMPzfBx|Dw#wcIxsSG7I&h{T+Sg{Mez&>5s2p9bn#h_1vyI UPkeXGf8TL*^H~0aCw9H~FGx%7Hvj+t delta 319 zcmV-F0l@yua_s_;Bo78+OGiWi000000Qp0^e~}>3}Q)b}Zp67}+$WQbqeD25kA$@AsbJAc?&VDW$s` zv;M1Lz*>ve8m%<|S(dHmlko67&kjH-g@>qN0Qd&kDhVMpB-+!~3I+;p2Q3yP&%cu{OJD8W**9Cm|2{TeG(>c~5?tzV893C929r9{Fp932oCUibC)odI0PL@p%Xt R^NIif002ovPDHLkV1jCskkSAE From 4679bdad77d257d40528b62b3e96e5d3d609c204 Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Sun, 22 Apr 2018 04:44:13 +0100 Subject: [PATCH 05/15] Kourend Library Plugin slight design tweak - Spaced out the item panels vertically and slightly horizontally. - Added a new header with the plugin's name, as it can be hard to know what this plugin does/is. - Restyled the reset button, it previously was a "Reset" text button, I switched it to an icon with a darker version that activates when clicked. - Added the icon images to the resource folder. - Added new method to SwingUtil that returns a darker version of an image - Used the image darkening method to give the refresh button a click feedback effect --- .../plugins/kourendlibrary/BookPanel.java | 6 +- .../kourendlibrary/KourendLibraryPanel.java | 79 ++++++++++++++++-- .../net/runelite/client/util/SwingUtil.java | 37 ++++++++ .../client/plugins/kourendlibrary/reset.png | Bin 0 -> 16069 bytes 4 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/reset.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/BookPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/BookPanel.java index b819731a03..5c169aab11 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/BookPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/BookPanel.java @@ -28,6 +28,7 @@ import java.awt.Color; import javax.swing.GroupLayout; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; import net.runelite.client.ui.FontManager; class BookPanel extends JPanel @@ -36,6 +37,9 @@ class BookPanel extends JPanel BookPanel(Book b) { + setBorder(new EmptyBorder(3, 3, 3, 3)); + setOpaque(false); + GroupLayout layout = new GroupLayout(this); this.setLayout(layout); @@ -74,4 +78,4 @@ class BookPanel extends JPanel { location.setForeground(target ? Color.GREEN : Color.WHITE); } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java index fb894ee2a8..1afaf27961 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Abex + * Copyright (c) 2018 Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,35 +26,71 @@ package net.runelite.client.plugins.kourendlibrary; import com.google.inject.Inject; +import java.awt.BorderLayout; +import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.io.IOException; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.imageio.ImageIO; import javax.inject.Singleton; +import javax.swing.BorderFactory; import javax.swing.GroupLayout; -import javax.swing.JButton; +import javax.swing.ImageIcon; +import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; +import net.runelite.client.util.SwingUtil; @Singleton public class KourendLibraryPanel extends PluginPanel { + private static final ImageIcon RESET_ICON; + private static final ImageIcon RESET_CLICK_ICON; + @Inject private Library library; private final HashMap bookPanels = new HashMap<>(); + static + { + try + { + synchronized (ImageIO.class) + { + BufferedImage resetIcon = ImageIO.read(KourendLibraryPanel.class.getResourceAsStream("reset.png")); + RESET_ICON = new ImageIcon(resetIcon); + RESET_CLICK_ICON = new ImageIcon(SwingUtil.grayscaleOffset(resetIcon, -100)); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + void init() { GroupLayout layout = new GroupLayout(this); setLayout(layout); + setBorder(new EmptyBorder(10, 10, 10, 10)); + setBackground(ColorScheme.DARK_GRAY_COLOR); JPanel books = new JPanel(new GridBagLayout()); + books.setOpaque(false); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; @@ -70,21 +107,45 @@ public class KourendLibraryPanel extends PluginPanel c.gridy++; }); - JButton reset = new JButton("Reset"); - reset.addActionListener(e -> + JLabel reset = new JLabel(RESET_ICON); + reset.addMouseListener(new MouseAdapter() { - library.reset(); - update(); + @Override + public void mousePressed(MouseEvent mouseEvent) + { + reset.setIcon(RESET_CLICK_ICON); + library.reset(); + update(); + } + + @Override + public void mouseReleased(MouseEvent mouseEvent) + { + reset.setIcon(RESET_ICON); + } }); + JPanel header = new JPanel(); + header.setOpaque(false); + header.setLayout(new BorderLayout()); + header.setBorder(new CompoundBorder( + BorderFactory.createMatteBorder(0, 0, 1, 0, new Color(58, 58, 58)), + BorderFactory.createEmptyBorder(0, 0, 10, 0))); + + JLabel pluginName = new JLabel("Kourend Library Plugin"); + pluginName.setForeground(Color.WHITE); + + header.add(reset, BorderLayout.EAST); + header.add(pluginName, BorderLayout.CENTER); + layout.setHorizontalGroup(layout.createParallelGroup() .addComponent(books) - .addComponent(reset) + .addComponent(header) ); layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(header) + .addGap(10) .addComponent(books) - .addGap(4) - .addComponent(reset) ); update(); @@ -134,4 +195,4 @@ public class KourendLibraryPanel extends PluginPanel } }); } -} +} \ No newline at end of file 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 41ddd1b9fb..90790cbf36 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 @@ -32,6 +32,7 @@ import java.awt.Frame; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; +import java.awt.RenderingHints; import java.awt.SystemTray; import java.awt.Toolkit; import java.awt.TrayIcon; @@ -41,6 +42,8 @@ import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; +import java.awt.image.LookupOp; +import java.awt.image.LookupTable; import java.util.Enumeration; import java.util.concurrent.Callable; import java.util.function.BiConsumer; @@ -95,6 +98,40 @@ public class SwingUtil System.setProperty("sun.awt.noerasebackground", "true"); } + /** + * Offsets an image in the grayscale (darkens/brightens) by an offset + */ + public static BufferedImage grayscaleOffset(BufferedImage image, int offset) + { + int numComponents = image.getColorModel().getNumComponents(); + int index = numComponents - 1; + + LookupTable lookup = new LookupTable(0, numComponents) + { + @Override + public int[] lookupPixel(int[] src, int[] dest) + { + if (dest[index] != 0) + { + dest[index] = dest[index] + offset; + if (dest[index] < 0) + { + dest[index] = 0; + } + else if (dest[index] > 255) + { + dest[index] = 255; + } + } + + return dest; + } + }; + + LookupOp op = new LookupOp(lookup, new RenderingHints(null)); + return op.filter(image, null); + } + /** * Converts a given color to it's hexidecimal equivalent. */ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/reset.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/reset.png new file mode 100644 index 0000000000000000000000000000000000000000..e94f6102a5cd22911c1e858557a9eb5c96decb4a GIT binary patch literal 16069 zcmeI3dsGuw8o0B2tiC#u%f(Fu=p4z6BtM`F$oYJivqP3 z>w{VqU*MyQU7uL1R%@-)f>l?g3P-!zR#3o4!P;8IE*#lA36F4rIiAz**?(rvfy{Tm z*WBN|-*@MqDTtUkZHW8d+z|vB5*DhB0>3ZQe}e~t|83t){T}?hXbz3DB8bN@`fmWT zdFx07;fy3Tnuv%b%0^j}C^I)qqv4t@l%6yY2x7mEFIrSmd-qoT`Pa(JBO+p=>V4`* zdV6}8mgkh@uFBn%OJ8G!$B&f@x#jQC_h>M>egG%?#F_H5<>lq(xNDay&#ljuly8oY zh&c@gmgeSuHF5lr0N`5Z!Qp5!Gr~e%Hv``Gjfz7a4VaZc~ z4JY546GBAlGp82h?I_oTU9)>I*JIO*udcRXiKjij{g54 z@7d@-V#J22b0%Ds$$XBIP3N1oDB=|+%Lwyg3%?=lR!&9r>AhcNtu9-+u%!L)q-EPb zuN|_(9}BEQd3&csSJYRU3aSbR7Y#4^VytwUGGuCaPsn$hW>m$Me(H5e5!_Oh z-Ru_W5$M$%-_hVsR#ja;lX~Oosek|Tu&?D&1^@Mk z-_c(eK04LhKK7yb-n9+Eh9%>k?SIxj1{rvodvWkT)?G#rB}&G`+G4|JC~?Z@kLjoc z!ryK*gNi~BRj}QR;VFcTn?NLzrXb$WCrV6rOhK|DuXCAg=DQ6AS}VoM3)h0qDPvEdP14P_y?QhymA z7s%vXxx!y4mCGgaH@PB#P>Kq~s93-kiIoz8QX=AZTs*ZqxKmkldS#S$YDYRC1@V$> zHnS2%)6>)a)5ZRjB@q=W6be)zLPa7zu;5!WOg7BUH(AFygLKBx5?0(onr$Ry;?i-k z1S-`Q#N*M4x~`6S8O>dZOx6x|fFf$g%&5>`fcE92!@GRUsTPCd(R4UU7ziU_vRQ$j zuung8XBX>o?~$@g-EU&BCgI_IJ9j>>(b#8dt8GdeK+u7-N7_zLYfOfjK%)pNm1@C> zDQRFT|{S~w8rV?*b2@SQh~WDH5LN1QI;5rGN_%qtotAU z=K#H{xD)4+CLNV-^;My;OZR$rMk>zYEA^Dch}qPn5lbXcvnf%9b`QE( zcI_&qhB8nV@K}Ues6zW1?$Zk#xDn3tR;{IM+-VqT;tB=hT_d=dxP)+QR%MvUYQs!8 z5vEmx0e_OzDFq37ksbr|2}~m8>j;UEkLhq7AIBBA6bn!Uh{RIII)sP2y7vmLrSMd` z;6P|+Z_-gX2;V(3nS{_w1s+dIhG}V+ukb>zYd+VS1$sC9Pn? zXSfssCCRx;#7F$llBOd0!s6E}_ z@G6u(P7LJ#U?Hqn+8+4NIPMO)smnOTj_ox4CwHI6@5C! z_2=lK=x}r{mCKu*qXwO$;Jp#NvZ7t@ti8&!tBd`aH{CP(GowPW*d!1TW|76k%m>o4 zxF8_RB8!We52R&rK|q*A78f%gNXz1afG~?JE@nQEmc<1DVHR0j%zPj%iwgq6EV8(m z`9NA07X*Y^WN|U`fwU|x2ne&t;$r3lX<1wl5N46Z#mooNvbZ21%p!}6nGd98aX~|du`BRuMHD@ zcb?2UnmlUlu~o9NO4E1q^MjXFUwoLIy<}K@OZC(Iu`%cq?{7m&j=eaDG}(ng1GJ$_=vaC6o1+wO6uZ*~O}L-ib}*o2{>H0~ z18WNk_`hlkJhmB^gqZB5`X4hBckMbl_ufw)>V!#N8TGvQB9Ec%PqwKCA!!pn)qHbz S`9At1+OR1zwFf3I%KI-(1ve%D literal 0 HcmV?d00001 From 1d388fa98fd64dc7fada9181779eb86a1eeb7dd0 Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Sun, 22 Apr 2018 17:37:33 +0100 Subject: [PATCH 06/15] News Feed Plugin slight design tweak - Added a header section, containing a title and a refresh icon button. - Increased the overall border from 5 to 10 pixels. - Changed the background color to a darker gray. --- .../client/plugins/feed/FeedPanel.java | 94 ++++++++++++++---- .../runelite/client/plugins/feed/reset.png | Bin 0 -> 16069 bytes 2 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/feed/reset.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPanel.java index 19e2e94c30..9b6d1e2229 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPanel.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Lotto + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,6 +29,7 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; +import java.awt.GridLayout; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.font.FontRenderContext; @@ -43,13 +45,16 @@ import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import lombok.extern.slf4j.Slf4j; +import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.PluginPanel; import net.runelite.client.util.LinkBrowser; +import net.runelite.client.util.SwingUtil; import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.feed.FeedItem; import net.runelite.http.api.feed.FeedItemType; @@ -63,8 +68,10 @@ import okhttp3.ResponseBody; @Slf4j class FeedPanel extends PluginPanel { - private static BufferedImage RUNELITE_ICON; - private static BufferedImage OSRS_ICON; + private static final ImageIcon RUNELITE_ICON; + private static final ImageIcon OSRS_ICON; + private static final ImageIcon RESET_ICON; + private static final ImageIcon RESET_ICON_CLICK; //used as a click effect (darker version of the reset icon) private static final Color TWEET_BACKGROUND = new Color(15, 15, 15); private static final Color OSRS_NEWS_BACKGROUND = new Color(36, 30, 19); @@ -74,6 +81,11 @@ class FeedPanel extends PluginPanel private static final int CONTENT_WIDTH = 148; private static final int TIME_WIDTH = 20; + /** + * Holds all feed items. + */ + private final JPanel feedContainer = new JPanel(); + private static final Comparator FEED_ITEM_COMPARATOR = (o1, o2) -> { if (o1.getType() != o2.getType()) @@ -97,24 +109,16 @@ class FeedPanel extends PluginPanel { synchronized (ImageIO.class) { - RUNELITE_ICON = ImageIO.read(FeedPanel.class.getResourceAsStream("runelite.png")); + BufferedImage reset = ImageIO.read(FeedPanel.class.getResourceAsStream("reset.png")); + RUNELITE_ICON = new ImageIcon(ImageIO.read(FeedPanel.class.getResourceAsStream("runelite.png"))); + OSRS_ICON = new ImageIcon(ImageIO.read(FeedPanel.class.getResourceAsStream("osrs.png"))); + RESET_ICON = new ImageIcon(reset); + RESET_ICON_CLICK = new ImageIcon(SwingUtil.grayscaleOffset(reset, -100)); } } catch (IOException e) { - log.warn("Client icon failed to load", e); - } - - try - { - synchronized (ImageIO.class) - { - OSRS_ICON = ImageIO.read(FeedPanel.class.getResourceAsStream("osrs.png")); - } - } - catch (IOException e) - { - log.warn("OSRS icon failed to load", e); + throw new RuntimeException(e); } } @@ -125,6 +129,56 @@ class FeedPanel extends PluginPanel { this.config = config; this.feedSupplier = feedSupplier; + + setBorder(new EmptyBorder(10, 10, 10, 10)); + setBackground(ColorScheme.DARK_GRAY_COLOR); + setLayout(new BorderLayout()); + + feedContainer.setLayout(new GridLayout(0, 1, 0, 4)); + feedContainer.setOpaque(false); + + /** + * This header contains the "News Feed" title and a refresh icon button. + */ + JPanel header = new JPanel(); + header.setOpaque(false); + header.setLayout(new BorderLayout()); + header.setBorder(new EmptyBorder(0, 0, 9, 0)); + + /** + * A refresh icon button, when clicked, it will swap icons for feedback effect and then call + * the rebuildFeed method. + */ + JLabel reset = new JLabel(); + reset.setIcon(RESET_ICON); + reset.setVerticalAlignment(SwingConstants.CENTER); + reset.setHorizontalAlignment(SwingConstants.CENTER); + reset.setToolTipText("Refresh"); + + reset.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + reset.setIcon(RESET_ICON_CLICK); + rebuildFeed(); + } + + @Override + public void mouseReleased(MouseEvent mouseEvent) + { + reset.setIcon(RESET_ICON); + } + }); + + JLabel title = new JLabel("News feed"); + title.setForeground(Color.WHITE); + + header.add(title, BorderLayout.WEST); + header.add(reset, BorderLayout.EAST); + + add(header, BorderLayout.NORTH); + add(feedContainer, BorderLayout.CENTER); } void rebuildFeed() @@ -138,7 +192,7 @@ class FeedPanel extends PluginPanel SwingUtilities.invokeLater(() -> { - removeAll(); + feedContainer.removeAll(); feed.getItems() .stream() @@ -207,14 +261,14 @@ class FeedPanel extends PluginPanel case OSRS_NEWS: if (OSRS_ICON != null) { - avatar.setIcon(new ImageIcon(OSRS_ICON)); + avatar.setIcon(OSRS_ICON); } avatarAndRight.setBackground(OSRS_NEWS_BACKGROUND); break; default: if (RUNELITE_ICON != null) { - avatar.setIcon(new ImageIcon(RUNELITE_ICON)); + avatar.setIcon(RUNELITE_ICON); } avatarAndRight.setBackground(BLOG_POST_BACKGROUND); break; @@ -296,7 +350,7 @@ class FeedPanel extends PluginPanel } }); - add(avatarAndRight); + feedContainer.add(avatarAndRight); } private String durationToString(Duration duration) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/feed/reset.png b/runelite-client/src/main/resources/net/runelite/client/plugins/feed/reset.png new file mode 100644 index 0000000000000000000000000000000000000000..e94f6102a5cd22911c1e858557a9eb5c96decb4a GIT binary patch literal 16069 zcmeI3dsGuw8o0B2tiC#u%f(Fu=p4z6BtM`F$oYJivqP3 z>w{VqU*MyQU7uL1R%@-)f>l?g3P-!zR#3o4!P;8IE*#lA36F4rIiAz**?(rvfy{Tm z*WBN|-*@MqDTtUkZHW8d+z|vB5*DhB0>3ZQe}e~t|83t){T}?hXbz3DB8bN@`fmWT zdFx07;fy3Tnuv%b%0^j}C^I)qqv4t@l%6yY2x7mEFIrSmd-qoT`Pa(JBO+p=>V4`* zdV6}8mgkh@uFBn%OJ8G!$B&f@x#jQC_h>M>egG%?#F_H5<>lq(xNDay&#ljuly8oY zh&c@gmgeSuHF5lr0N`5Z!Qp5!Gr~e%Hv``Gjfz7a4VaZc~ z4JY546GBAlGp82h?I_oTU9)>I*JIO*udcRXiKjij{g54 z@7d@-V#J22b0%Ds$$XBIP3N1oDB=|+%Lwyg3%?=lR!&9r>AhcNtu9-+u%!L)q-EPb zuN|_(9}BEQd3&csSJYRU3aSbR7Y#4^VytwUGGuCaPsn$hW>m$Me(H5e5!_Oh z-Ru_W5$M$%-_hVsR#ja;lX~Oosek|Tu&?D&1^@Mk z-_c(eK04LhKK7yb-n9+Eh9%>k?SIxj1{rvodvWkT)?G#rB}&G`+G4|JC~?Z@kLjoc z!ryK*gNi~BRj}QR;VFcTn?NLzrXb$WCrV6rOhK|DuXCAg=DQ6AS}VoM3)h0qDPvEdP14P_y?QhymA z7s%vXxx!y4mCGgaH@PB#P>Kq~s93-kiIoz8QX=AZTs*ZqxKmkldS#S$YDYRC1@V$> zHnS2%)6>)a)5ZRjB@q=W6be)zLPa7zu;5!WOg7BUH(AFygLKBx5?0(onr$Ry;?i-k z1S-`Q#N*M4x~`6S8O>dZOx6x|fFf$g%&5>`fcE92!@GRUsTPCd(R4UU7ziU_vRQ$j zuung8XBX>o?~$@g-EU&BCgI_IJ9j>>(b#8dt8GdeK+u7-N7_zLYfOfjK%)pNm1@C> zDQRFT|{S~w8rV?*b2@SQh~WDH5LN1QI;5rGN_%qtotAU z=K#H{xD)4+CLNV-^;My;OZR$rMk>zYEA^Dch}qPn5lbXcvnf%9b`QE( zcI_&qhB8nV@K}Ues6zW1?$Zk#xDn3tR;{IM+-VqT;tB=hT_d=dxP)+QR%MvUYQs!8 z5vEmx0e_OzDFq37ksbr|2}~m8>j;UEkLhq7AIBBA6bn!Uh{RIII)sP2y7vmLrSMd` z;6P|+Z_-gX2;V(3nS{_w1s+dIhG}V+ukb>zYd+VS1$sC9Pn? zXSfssCCRx;#7F$llBOd0!s6E}_ z@G6u(P7LJ#U?Hqn+8+4NIPMO)smnOTj_ox4CwHI6@5C! z_2=lK=x}r{mCKu*qXwO$;Jp#NvZ7t@ti8&!tBd`aH{CP(GowPW*d!1TW|76k%m>o4 zxF8_RB8!We52R&rK|q*A78f%gNXz1afG~?JE@nQEmc<1DVHR0j%zPj%iwgq6EV8(m z`9NA07X*Y^WN|U`fwU|x2ne&t;$r3lX<1wl5N46Z#mooNvbZ21%p!}6nGd98aX~|du`BRuMHD@ zcb?2UnmlUlu~o9NO4E1q^MjXFUwoLIy<}K@OZC(Iu`%cq?{7m&j=eaDG}(ng1GJ$_=vaC6o1+wO6uZ*~O}L-ib}*o2{>H0~ z18WNk_`hlkJhmB^gqZB5`X4hBckMbl_ufw)>V!#N8TGvQB9Ec%PqwKCA!!pn)qHbz S`9At1+OR1zwFf3I%KI-(1ve%D literal 0 HcmV?d00001 From dcd241e3bf3213f06bdda5b560c74c5b6517e79e Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Sun, 22 Apr 2018 17:55:03 +0100 Subject: [PATCH 07/15] Notes Plugin slight design tweak - Increased the padding on the overall panel and the title. - Recolored the background and text editor. - Wrapped the text editor in a panel to prevent substance's hover effects. - Added 10px padding to the text editor --- .../client/plugins/notes/NotesPanel.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPanel.java index d0cd509e4b..b0d6039b30 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPanel.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Charlie Waters + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,13 +25,17 @@ */ package net.runelite.client.plugins.notes; +import java.awt.Color; import javax.swing.BorderFactory; import javax.swing.JTextArea; import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import lombok.extern.slf4j.Slf4j; +import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; import java.awt.BorderLayout; @@ -50,14 +55,24 @@ public class NotesPanel extends PluginPanel getParent().add(this, BorderLayout.CENTER); setLayout(new BorderLayout()); - setBorder(BorderFactory.createEmptyBorder(2, 6, 6, 6)); + setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + setBackground(ColorScheme.DARK_GRAY_COLOR); final JLabel notesHeader = new JLabel("Notes"); + notesHeader.setForeground(Color.WHITE); + notesHeader.setBorder(new EmptyBorder(1, 0, 10, 0)); + add(notesHeader, BorderLayout.NORTH); notesEditor.setLineWrap(true); notesEditor.setWrapStyleWord(true); + JPanel notesContainer = new JPanel(); + notesContainer.setLayout(new BorderLayout()); + notesContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + notesEditor.setOpaque(false); + // load note text String data = config.notesData(); notesEditor.setText(data); @@ -90,7 +105,10 @@ public class NotesPanel extends PluginPanel } } }); - add(notesEditor, BorderLayout.CENTER); + notesContainer.add(notesEditor, BorderLayout.CENTER); + notesContainer.setBorder(new EmptyBorder(10, 10, 10, 10)); + + add(notesContainer, BorderLayout.CENTER); } void setNotes(String data) From 6ec0d60ec4e503ab384718d42fa268f7fd339459 Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Sun, 22 Apr 2018 19:34:27 +0100 Subject: [PATCH 08/15] Configs slight design tweak - Updated the search bar to the new icon text field component. - Added new on/off icons based on the material design style. - Recoloured the background. - Recoloured the plugin name labels. - Replaced the config/toggle buttons for icon labels. - Hid config button if no config was found, instead of disabling it. - Left aligned the header title. - Added new ComboBoxListRenderer to prevent substance's ugly coloring. - Changed the panel title to "Configuration" - Used deathbeam's new layout manager DynamicGridLayout --- .../client/plugins/config/ConfigPanel.java | 183 ++++++++++----- .../runelite/client/ui/DynamicGridLayout.java | 215 ++++++++++++++++++ .../ui/components/ComboBoxListRenderer.java | 65 ++++++ .../client/ui/components/IconTextField.java | 6 + .../plugins/config/config_edit_icon.png | Bin 0 -> 15088 bytes .../client/plugins/config/disabled.png | Bin 335 -> 0 bytes .../client/plugins/config/enabled.png | Bin 417 -> 0 bytes .../client/plugins/config/switchers/off.png | Bin 0 -> 15659 bytes .../client/plugins/config/switchers/on.png | Bin 0 -> 15669 bytes 9 files changed, 406 insertions(+), 63 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/config/config_edit_icon.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/config/disabled.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/config/enabled.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/config/switchers/off.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/config/switchers/on.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java index 61b1e8b105..0a0b91256e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java @@ -37,7 +37,6 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Comparator; import java.util.Map; @@ -61,6 +60,7 @@ import javax.swing.JTextField; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; @@ -75,16 +75,22 @@ import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginInstantiationException; import net.runelite.client.plugins.PluginManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.ComboBoxListRenderer; +import net.runelite.client.ui.components.IconTextField; @Slf4j public class ConfigPanel extends PluginPanel { private static final int TEXT_FIELD_WIDTH = 7; private static final int SPINNER_FIELD_WIDTH = 6; - private static BufferedImage CONFIG_ICON; - private static BufferedImage UNCHECK_ICON; - private static BufferedImage CHECK_ICON; + + private static final ImageIcon CONFIG_ICON; + private static final ImageIcon ON_SWITCHER; + private static final ImageIcon OFF_SWITCHER; + private static final ImageIcon SEARCH; static { @@ -92,14 +98,15 @@ public class ConfigPanel extends PluginPanel { synchronized (ImageIO.class) { - CONFIG_ICON = ImageIO.read(ConfigPanel.class.getResourceAsStream("config_icon.png")); - UNCHECK_ICON = ImageIO.read(ConfigPanel.class.getResourceAsStream("disabled.png")); - CHECK_ICON = ImageIO.read(ConfigPanel.class.getResourceAsStream("enabled.png")); + CONFIG_ICON = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("config_edit_icon.png"))); + ON_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/on.png"))); + OFF_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/off.png"))); + SEARCH = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search.png"))); } } catch (IOException e) { - log.warn("Failed to read icon", e); + throw new RuntimeException(e); } } @@ -107,7 +114,7 @@ public class ConfigPanel extends PluginPanel private final ConfigManager configManager; private final ScheduledExecutorService executorService; private final RuneLiteConfig runeLiteConfig; - private final JTextField searchBar = new JTextField(); + private final IconTextField searchBar = new IconTextField(); private Map children = new TreeMap<>(); private int scrollBarPosition = 0; @@ -119,6 +126,10 @@ public class ConfigPanel extends PluginPanel this.executorService = executorService; this.runeLiteConfig = runeLiteConfig; + searchBar.setIcon(SEARCH); + searchBar.setPreferredSize(new Dimension(100, 30)); + searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR); + searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); searchBar.getDocument().addDocumentListener(new DocumentListener() { @Override @@ -140,6 +151,10 @@ public class ConfigPanel extends PluginPanel } }); + setBorder(new EmptyBorder(10, 10, 10, 10)); + setLayout(new DynamicGridLayout(0, 1, 0, 5)); + setBackground(ColorScheme.DARK_GRAY_COLOR); + rebuildPluginList(); openConfigList(); } @@ -150,42 +165,54 @@ public class ConfigPanel extends PluginPanel Map newChildren = new TreeMap<>(); pluginManager.getPlugins().stream() - .filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden()) - .sorted(Comparator.comparing(left -> left.getClass().getAnnotation(PluginDescriptor.class).name())) - .forEach(plugin -> - { - final Config pluginConfigProxy = pluginManager.getPluginConfigProxy(plugin); - final String pluginName = plugin.getClass().getAnnotation(PluginDescriptor.class).name(); + .filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden()) + .sorted(Comparator.comparing(left -> left.getClass().getAnnotation(PluginDescriptor.class).name())) + .forEach(plugin -> + { + final Config pluginConfigProxy = pluginManager.getPluginConfigProxy(plugin); + final String pluginName = plugin.getClass().getAnnotation(PluginDescriptor.class).name(); - final JPanel groupPanel = buildGroupPanel(); - groupPanel.add(new JLabel(pluginName), BorderLayout.CENTER); + final JPanel groupPanel = buildGroupPanel(); - final JPanel buttonPanel = new JPanel(); - buttonPanel.setLayout(new GridLayout(1, 2, 3, 0)); - groupPanel.add(buttonPanel, BorderLayout.LINE_END); + JLabel name = new JLabel(pluginName); + name.setForeground(Color.WHITE); - final JButton editConfigButton = buildConfigButton(pluginConfigProxy); - buttonPanel.add(editConfigButton); + groupPanel.add(name, BorderLayout.CENTER); - final JButton toggleButton = buildToggleButton(plugin); - buttonPanel.add(toggleButton); + final JPanel buttonPanel = new JPanel(); + buttonPanel.setOpaque(false); + buttonPanel.setLayout(new GridLayout(1, 2)); + groupPanel.add(buttonPanel, BorderLayout.LINE_END); - newChildren.put(pluginName, groupPanel); - }); + final JLabel editConfigButton = buildConfigButton(pluginConfigProxy); + buttonPanel.add(editConfigButton); + final JLabel toggleButton = buildToggleButton(plugin); + toggleButton.setHorizontalAlignment(SwingConstants.RIGHT); + buttonPanel.add(toggleButton); + + newChildren.put(pluginName, groupPanel); + }); final JPanel groupPanel = buildGroupPanel(); - groupPanel.add(new JLabel("RuneLite"), BorderLayout.CENTER); + + JLabel name = new JLabel("RuneLite"); + name.setForeground(Color.WHITE); + + groupPanel.add(name, BorderLayout.CENTER); final JPanel buttonPanel = new JPanel(); - buttonPanel.setLayout(new GridLayout(1, 2, 3, 0)); + buttonPanel.setOpaque(false); + buttonPanel.setLayout(new GridLayout(1, 2)); groupPanel.add(buttonPanel, BorderLayout.LINE_END); - final JButton editConfigButton = buildConfigButton(runeLiteConfig); + final JLabel editConfigButton = buildConfigButton(runeLiteConfig); buttonPanel.add(editConfigButton); - final JButton toggleButton = buildToggleButton(null); + final JLabel toggleButton = buildToggleButton(null); + toggleButton.setVisible(false); buttonPanel.add(toggleButton); + newChildren.put("RuneLite", groupPanel); children = newChildren; @@ -197,15 +224,17 @@ public class ConfigPanel extends PluginPanel // Create base panel for the config button and enabled/disabled button final JPanel groupPanel = new JPanel(); groupPanel.setLayout(new BorderLayout(3, 0)); + groupPanel.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH, 20)); + groupPanel.setOpaque(false); return groupPanel; } - private JButton buildConfigButton(Config config) + private JLabel buildConfigButton(Config config) { // Create edit config button and disable it by default - final JButton editConfigButton = new JButton(new ImageIcon(CONFIG_ICON)); - editConfigButton.setPreferredSize(new Dimension(32, 0)); - editConfigButton.setEnabled(false); + final JLabel editConfigButton = new JLabel(CONFIG_ICON); + editConfigButton.setPreferredSize(new Dimension(25, 0)); + editConfigButton.setVisible(false); // If we have configuration proxy enable the button and add edit config listener if (config != null) @@ -215,8 +244,15 @@ public class ConfigPanel extends PluginPanel if (!configEmpty) { - editConfigButton.addActionListener(ae -> openGroupConfigPanel(config, configDescriptor, configManager)); - editConfigButton.setEnabled(true); + editConfigButton.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + openGroupConfigPanel(config, configDescriptor, configManager); + } + }); + editConfigButton.setVisible(true); editConfigButton.setToolTipText("Edit plugin configuration"); } } @@ -224,11 +260,11 @@ public class ConfigPanel extends PluginPanel return editConfigButton; } - private JButton buildToggleButton(Plugin plugin) + private JLabel buildToggleButton(Plugin plugin) { // Create enabling/disabling button - final JButton toggleButton = new JButton(new ImageIcon(CHECK_ICON)); - toggleButton.setPreferredSize(new Dimension(32, 0)); + final JLabel toggleButton = new JLabel(ON_SWITCHER); + toggleButton.setPreferredSize(new Dimension(25, 0)); if (plugin == null) { @@ -238,36 +274,43 @@ public class ConfigPanel extends PluginPanel highlightButton(toggleButton, pluginManager.isPluginEnabled(plugin)); - toggleButton.addActionListener(e -> executorService.submit(() -> + toggleButton.addMouseListener(new MouseAdapter() { - final boolean enabled = pluginManager.isPluginEnabled(plugin); - pluginManager.setPluginEnabled(plugin, !enabled); - - try + @Override + public void mousePressed(MouseEvent mouseEvent) { - if (enabled) + executorService.submit(() -> { - pluginManager.stopPlugin(plugin); - } - else - { - pluginManager.startPlugin(plugin); - } - } - catch (PluginInstantiationException ex) - { - log.warn("Error during starting/stopping plugin {}", plugin.getClass().getSimpleName(), ex); - } + final boolean enabled = pluginManager.isPluginEnabled(plugin); + pluginManager.setPluginEnabled(plugin, !enabled); - highlightButton(toggleButton, !enabled); - })); + try + { + if (enabled) + { + pluginManager.stopPlugin(plugin); + } + else + { + pluginManager.startPlugin(plugin); + } + } + catch (PluginInstantiationException ex) + { + log.warn("Error during starting/stopping plugin {}", plugin.getClass().getSimpleName(), ex); + } + + highlightButton(toggleButton, !enabled); + }); + } + }); return toggleButton; } - private void highlightButton(JButton button, boolean enabled) + private void highlightButton(JLabel button, boolean enabled) { - button.setIcon(enabled ? new ImageIcon(CHECK_ICON) : new ImageIcon(UNCHECK_ICON)); + button.setIcon(enabled ? ON_SWITCHER : OFF_SWITCHER); button.setToolTipText(enabled ? "Disable plugin" : "Enable plugin"); } @@ -301,7 +344,11 @@ public class ConfigPanel extends PluginPanel private void openConfigList() { removeAll(); - add(new JLabel("Plugin Configuration", SwingConstants.CENTER)); + + JLabel title = new JLabel("Configuration", SwingConstants.LEFT); + title.setForeground(Color.WHITE); + + add(title); add(searchBar); onSearchBarChanged(); @@ -331,6 +378,7 @@ public class ConfigPanel extends PluginPanel if (component instanceof JCheckBox) { JCheckBox checkbox = (JCheckBox) component; + checkbox.setOpaque(false); configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), "" + checkbox.isSelected()); } @@ -355,6 +403,8 @@ public class ConfigPanel extends PluginPanel if (component instanceof JComboBox) { JComboBox jComboBox = (JComboBox) component; + jComboBox.setRenderer(new ComboBoxListRenderer()); + jComboBox.setForeground(Color.WHITE); configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), ((Enum) jComboBox.getSelectedItem()).name()); } } @@ -376,6 +426,7 @@ public class ConfigPanel extends PluginPanel } JPanel item = new JPanel(); + item.setOpaque(false); item.setLayout(new BorderLayout()); name = cid.getItem().name(); JLabel configEntryName = new JLabel(name); @@ -385,6 +436,8 @@ public class ConfigPanel extends PluginPanel if (cid.getType() == boolean.class) { JCheckBox checkbox = new JCheckBox(); + checkbox.setOpaque(false); + checkbox.setBackground(ColorScheme.LIGHT_GRAY_COLOR); checkbox.setSelected(Boolean.parseBoolean(configManager.getConfiguration(cd.getGroup().keyName(), cid.getItem().keyName()))); checkbox.addActionListener(ae -> changeConfiguration(config, checkbox, cd, cid)); @@ -462,6 +515,7 @@ public class ConfigPanel extends PluginPanel if (cid.getType() == Dimension.class) { JPanel dimensionPanel = new JPanel(); + dimensionPanel.setOpaque(false); dimensionPanel.setLayout(new BorderLayout()); String str = configManager.getConfiguration(cd.getGroup().keyName(), cid.getItem().keyName()); @@ -482,7 +536,7 @@ public class ConfigPanel extends PluginPanel heightSpinnerTextField.setColumns(4); ChangeListener listener = e -> - configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), widthSpinner.getValue() + "x" + heightSpinner.getValue()); + configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), widthSpinner.getValue() + "x" + heightSpinner.getValue()); widthSpinner.addChangeListener(listener); heightSpinner.addChangeListener(listener); @@ -498,6 +552,9 @@ public class ConfigPanel extends PluginPanel { Class type = (Class) cid.getType(); JComboBox box = new JComboBox(type.getEnumConstants()); + box.setPreferredSize(new Dimension(box.getPreferredSize().width, 25)); + box.setRenderer(new ComboBoxListRenderer()); + box.setForeground(Color.WHITE); box.setFocusable(false); box.setPrototypeDisplayValue("XXXXXXXX"); //sorry but this is the way to keep the size of the combobox in check. try @@ -541,4 +598,4 @@ public class ConfigPanel extends PluginPanel revalidate(); getScrollPane().getVerticalScrollBar().setValue(0); } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java b/runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java new file mode 100644 index 0000000000..8be06a706c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Insets; +import java.util.function.Function; + +/** + * Grid layout implementation with support for cells with unequal size. + * + * See https://www.javaworld.com/article/2077486/core-java/java-tip-121--flex-your-grid-layout.html + */ +public class DynamicGridLayout extends GridLayout +{ + public DynamicGridLayout() + { + this(1, 0, 0, 0); + } + + public DynamicGridLayout(int rows, int cols) + { + this(rows, cols, 0, 0); + } + + public DynamicGridLayout(int rows, int cols, int hgap, int vgap) + { + super(rows, cols, hgap, vgap); + } + + @Override + public Dimension preferredLayoutSize(Container parent) + { + synchronized (parent.getTreeLock()) + { + return calculateSize(parent, Component::getPreferredSize); + } + } + + @Override + public Dimension minimumLayoutSize(Container parent) + { + synchronized (parent.getTreeLock()) + { + return calculateSize(parent, Component::getMinimumSize); + } + } + + @Override + public void layoutContainer(Container parent) + { + synchronized (parent.getTreeLock()) + { + final Insets insets = parent.getInsets(); + final int ncomponents = parent.getComponentCount(); + int nrows = getRows(); + int ncols = getColumns(); + + if (ncomponents == 0) + { + return; + } + + if (nrows > 0) + { + ncols = (ncomponents + nrows - 1) / nrows; + } + else + { + nrows = (ncomponents + ncols - 1) / ncols; + } + + final int hgap = getHgap(); + final int vgap = getVgap(); + + // scaling factors + final Dimension pd = preferredLayoutSize(parent); + final double sw = (1.0 * parent.getWidth()) / pd.width; + final double sh = (1.0 * parent.getHeight()) / pd.height; + + final int[] w = new int[ncols]; + final int[] h = new int[nrows]; + + // calculate dimensions for all components + apply scaling + for (int i = 0; i < ncomponents; i++) + { + final int r = i / ncols; + final int c = i % ncols; + final Component comp = parent.getComponent(i); + final Dimension d = comp.getPreferredSize(); + d.width = (int) (sw * d.width); + d.height = (int) (sh * d.height); + + if (w[c] < d.width) + { + w[c] = d.width; + } + + if (h[r] < d.height) + { + h[r] = d.height; + } + } + + // Apply new bounds to all child components + for (int c = 0, x = insets.left; c < ncols; c++) + { + for (int r = 0, y = insets.top; r < nrows; r++) + { + int i = r * ncols + c; + + if (i < ncomponents) + { + parent.getComponent(i).setBounds(x, y, w[c], h[r]); + } + + y += h[r] + vgap; + } + + x += w[c] + hgap; + } + } + } + + /** + * Calculate outer size of the layout based on it's children and sizer + * @param parent parent component + * @param sizer functioning returning dimension of the child component + * @return outer size + */ + private Dimension calculateSize(final Container parent, final Function sizer) + { + final int ncomponents = parent.getComponentCount(); + int nrows = getRows(); + int ncols = getColumns(); + + if (nrows > 0) + { + ncols = (ncomponents + nrows - 1) / nrows; + } + else + { + nrows = (ncomponents + ncols - 1) / ncols; + } + + final int[] w = new int[ncols]; + final int[] h = new int[nrows]; + + // Calculate dimensions for all components + for (int i = 0; i < ncomponents; i++) + { + final int r = i / ncols; + final int c = i % ncols; + final Component comp = parent.getComponent(i); + final Dimension d = sizer.apply(comp); + + if (w[c] < d.width) + { + w[c] = d.width; + } + + if (h[r] < d.height) + { + h[r] = d.height; + } + } + + // Calculate total width and height of the layout + int nw = 0; + + for (int j = 0; j < ncols; j++) + { + nw += w[j]; + } + + int nh = 0; + + for (int i = 0; i < nrows; i++) + { + nh += h[i]; + } + + final Insets insets = parent.getInsets(); + + // Apply insets and horizontal and vertical gap to layout + return new Dimension( + insets.left + insets.right + nw + (ncols - 1) * getHgap(), + insets.top + insets.bottom + nh + (nrows - 1) * getVgap()); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java new file mode 100644 index 0000000000..c97e128374 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017, Psikoi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.components; + +import java.awt.Color; +import java.awt.Component; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; +import javax.swing.border.EmptyBorder; +import net.runelite.client.ui.ColorScheme; + +/** + * A custom list renderer to avoid substance's weird coloring. + * Substance was making selected items' foreground color black, this + * was very hard to see in the dark gray background, this makes the selected + * item white and adds some padding to the elements for more readable list. + */ +public final class ComboBoxListRenderer extends JLabel implements ListCellRenderer +{ + + @Override + public Component getListCellRendererComponent(JList list, Object o, int index, boolean isSelected, boolean cellHasFocus) + { + if (isSelected) + { + setBackground(ColorScheme.DARK_GRAY_COLOR); + setForeground(Color.WHITE); + } + else + { + setBackground(list.getBackground()); + setForeground(ColorScheme.LIGHT_GRAY_COLOR); + } + + setBorder(new EmptyBorder(5, 5, 5, 0)); + + String text = (String) o.toString(); + setText(text); + + return this; + } +} \ 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 index e016215756..0734851fb0 100644 --- 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 @@ -37,6 +37,7 @@ import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; +import javax.swing.text.Document; import net.runelite.client.ui.ColorScheme; /** @@ -160,4 +161,9 @@ public class IconTextField extends JPanel } } + public Document getDocument() + { + return textField.getDocument(); + } + } \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/config/config_edit_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/config/config_edit_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d88ec51f71de12f62da20dbb99138f6cdee0f152 GIT binary patch literal 15088 zcmeI3TZkJ~7{{lexW;ZRRa@Pm;qzE54jS zF5mf|@B8Nb&iTH~%RD>OTBLxhn3RCSy6U}Cr{N8pG=u6ONtFUI-h2*98&n8*R zC?pRlWue@hgp1bHnhOuE%~bTYC0#X=dy9#DTSEaF&r`SL6~>9~+pQW;JcGFdjOrbH#1m9uxVk{~L)kml0@C#5x6&}512ypqKP z`jvMLQ=2Ot=%hndA-U-LO^xSUtyZd)PC0Ik7gbf|1&Nm=4$a`aRoe$`&h~aiLAr5D z(9>P3>06G?25~{vS@sLbWRR%$>a45L>?N|j4m(7VZ-XW;rUZVNlcD!~n#*oIyfj1S zVI4N0?R&^i9O~CxbbQBKbVj7?$s-e^Jt>!axrbhFqcJqK=kH%Z2s%iIdU}=BCgkU! z=PbKA+`odBvTNAteaq}`&bml}GV)rgf7uGQbqYTw0e* zrMs3PVFpr7SspsAtFcpz&T`n<4#LOQk9LK+6?Uj5a6K5*Um@8oz+Ox1b*#WAtYZ!6 zL2oPSL3uz#Vclr|vU;8!H*0V^H-;dJA9Ej_qqPWaQh7z8!dat(c#i3}fD8B4&I%0Z-l6`#ZRnq3eG%9-XcYPG0qzcLZ0Dl= zTwht;Y8|Y^eDLR_p(;iv3WU{zn&7}#KXWvHKB&g}ayUOL3FyU(!Dq)cF5|8XWY@Ti zyY@XFvMs+Tb+Tc@F|XhsG-~e$(ajz#W)vljIu|~Sd(2vGSf4Lhs1JKVKM`{sA6yvf zxG>5wCUhL5t%}{2g*6zBqMJT+zsdJ*I0wqJ*Tt^pkN%3TW>hQ|O@aX-n-mwB52jLF z7!a~aagq68D#e8XA)6EznGdE?To@3tNpX?+U@FCh0U?_d7nu*HQd}4ivPp4~`Cux= zg#jU(6c?EfrczuO5VA>ek@;XM#f1SOn-mwB52jLF7!a~aagq68D#e8XA)6EznGdE? zTo@3tNpX?+U@FCh0U?_d7nu*HQd}4ivPp4~`Cux=g#jU(6c?EfrczuO5VA>ek@;XM z#f1SOn-mwB52jLF7!a~aagq68D#e8XA)6EznGdE?To@3tNyL@tKf(!Z^xS3(J)pVt z#%nL4hdWt)YOc&MYZDA}_$h|D{15v6kzwY>m`%6LGmQ2!!|ZUre)#rdXzml!rG1t5 zH}Agm;P@xP=4;14K9-o+nt+?PZMlv$zmnGeJomxM`R$FlU;jRF{JV+M=YBYSicOrH zxx7Vr`n|^wk7f4ke5?M;jw2ICk1l*Md)vilF1}g0@l5T)b6Zo#zaKmO(LJBP{`RTd zt2=g|eg3nt-)`Ri!pT1_iRZZ^=gvIx;hx_&F<)}d+1iDl?+Bhro!&oFdgtCppZphq CxLUOU literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/config/disabled.png b/runelite-client/src/main/resources/net/runelite/client/plugins/config/disabled.png deleted file mode 100644 index 160d431a9512ba88d81280c55c0a6811b5233ef9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 335 zcmeAS@N?(olHy`uVBq!ia0vp^{2?0v z(btiIVPjv-@4(4GzCyA`kS_y6l_~>6Lo)-z&;LOBB?CjL0RzLU1O^7H84L{K`IF+0 zx&hT5^>lFzskpTycsFmefxzC)8bW~zt;eKOC-zL_xad&Ky|MATgPelzf=5d?p3GSM6Ft;+aPHg<(rY!-9Tn7J+9W9hEe&okq{ a$})(|y=9e~bA=n|AqG!ZKbLh*2~7ZrZFdj= diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/config/enabled.png b/runelite-client/src/main/resources/net/runelite/client/plugins/config/enabled.png deleted file mode 100644 index a48cfcd5cced8f49f31dd8f8a17d735bd2f5c8b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 417 zcmV;S0bc%zP)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ-7fD1xRCwB?ld(zzQ4mGXUDg#Fi?o)0goR+0E`Onw zSljsn1}qFnIxDRd>}`ELI@$v!dj>?vCxeH8^nDHW?-6m_uMx#yeBi` zzoBtN-!G74aZT=|VCkYr5C%vxXA)_3ryjp#e8X**_k5jbxv{@00000 LNkvXXu0mjfT$7+V diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/config/switchers/off.png b/runelite-client/src/main/resources/net/runelite/client/plugins/config/switchers/off.png new file mode 100644 index 0000000000000000000000000000000000000000..ff25cc611a7c1dc7e0c79ae35183214f93ba534a GIT binary patch literal 15659 zcmeI3e{dA#8OJvQg3(|E9h6Z#52}H1x4U<_+}(0H@>3J#AY4*Buw~fWeJ@#aw|m^} zkxNKv5YbqbS{hr&7DXT^N|>aIE!CD-Gf`k#v<5>pRfIt-lsXQjX!()8dw01XZ<2R1 zQ~OWny_vh)=Y8Jq^X~h3pZD4KpM9))@jW@&GqWj*%BiX>cfbq{ZUVDgN{{XGYk z2CFY!HB`L%^s(GKHf)&k+}{S@J{fxJrK6D-`F1|H;fT!r`FxB0V()b|)O**w+Ar^& zN*$lG>Fc*n4QBBa<25XJTot6QR`W`eWPw(@lffDGkb4lHmrNOWH4(dCk+%k zsE(EYpo{0edpRd>?!dW{7Rq<{d<*kAWq$VulbDFQE9=23&RzGr8pA6;166PQV&YF6 zvj8_!3@w~G{pmYj-oD-V)X?M}@9&SwT&VNGrv z-V<9sJ$1YRF7*B zCny0UFDhPOj0S>`H$^!XMuWW2092h9)JbxY{>wM^>vfV?q`%+dVqC#8P%l+Bhd@p9 z;##4(L9mPZh0bh8l!E~RK;?DOfL{)C(IR~;E(fo*W?HX{bx|9N^d;JWI*+SbSEhu3 z&SESy2uz_>XSEwyi`80Sov$-7tc7OGw3#uO%v=G(6_|91kKUOLuN)!K$GOYzP7DXF zB7ME8205B;Y-}_(nvF`Rj%Mw4JI$DAlgR*k7{X1m%0~@ycy>I7&^Z3MMZ$&lj*?;W{cS?`s@a?$QDA7z-F+stY8pW zU}da6o84rO(}V-11sgHYl8^-J67Nszqs>KxgRvG?uosFpL!pTg3_h{I2Pedu4HmY* z%X)1#Yk@@=p35j(sm9cpj#LdQQ~XNErHH_3cF?0W zkE((P%VFwKz=#xpAIbQdcX@m6?tgvVAod2St1MUut# z+q_84=-Z5n#3F+rK=3BPh3A8K2`&T(-Xys2d=M|eg#f{u1Q(tU;w88cAb6AD!t+7A z1Q!AXZxUR1K8Tm#LV(~+f(y?F@e*7J5WGon;rSq5f(rqHHwi90AH+*=AwcjZ!G-68 zcnK~92;L;P@O%(2!G!?9n*Ggqz>3?Sf-mbbRplkM(U1Dui|*?!&VA(hocG<#mWO&jpY!y_WB(dB z+U7ku)YWyv57%$p8+q+l)80RLuus1%d-K*EkNx5M;VDLGu&et(N&Clh^0F6O7o7Xk zgmc~7=HIvO<6E!v4E}cd#4O5vFlW_dPiN_`d-xkGW}NumTp)bLTz6O5ysxslE}qw0!|?78}pp4+zWKD*g1FQNJa*Lu2pceYMimGew@=@ab5z-3FH|MeYqkq`*cjnB#ooA=rmfd!HXV-`S?r?40J8R8L3%W|%-FF2}I}R%=+@Gvpn?J|( z!R(IyeXh>-AKm)SKH2kG-LdTGtFvY^Z6~6KFI+x)?#;+RWGz*()bZrq+Yik@v}ygL z#&^D!+RpE9X!)wg@!kYq{?JX)-50p3&0m&WIY0RumU^!Cft{BgUD~g`j9XQ)xO`9P Hs)zmq-qc~w literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/config/switchers/on.png b/runelite-client/src/main/resources/net/runelite/client/plugins/config/switchers/on.png new file mode 100644 index 0000000000000000000000000000000000000000..43028e1716185029617ba25dbad6d574040502b1 GIT binary patch literal 15669 zcmeI3e{d7W701sM8Za2#gcL*C<`6V7wxzq%FH46kY%C`hAW&q}*zT0!ba%2ZNOwYa zwq?g54o(9SAb&wVK@bJrT2BCc&j;^0)4vQpzY-EI zg0C6T+D4TiX5Oa1^N44DT0{_ecB#7B?{85wMQu@{q_?`7jK-9Z6c!00ey;cN11sM0 z&#gSZ?fF4}>C}zU_z#PV4}QG<=nKZ0!dK2L9QgeUyH-fIu3L9z)1$k;eapP|H~I(e zi~4U&8L8a(@kfOVdwQln`yZzkB{q-!JTL=qziha?}rh zShD?R9}W*5jnAL0aGil)|FQJdKXgBK`T5>&?RtIMwF^}L(KR4Dcn=})={V8WSUAB);3+XIMu0>G56_JI2+`4vmX_x{M%q2$ z>@_KrS)3fL?wL>~#-#~lx+f`1q(X9qTp<70>X^uBN~~T{!tP|2Wy%Do5_(pV#rI3H zptP$?Tof$Htam1w=;1Wc4U`TeZ8b7XJx#M_8*4F_(hN(}SwWILp%^}p`vRwNlZlek z7mlL{QmA86Q8GPqWB7cmS5`Gn=0&f^4d!l=B!Lwz_7LX`g4qe>jL~AY^2T70u^2;O z$|0+jHira9Vv@P?F`+#Q->N4P2%X&cg2IFF>GWXjL7Fx@7%+yw*a1krM<@j560+NjX2EJPI_yrfkqHHZ z_MkvJ9rpBG#tAcKs9H${+0>DhWgyoQm8VYJnA)!|2XTmz;D( z(KU$;&T_n7d)&O9zM|lwa#}Nwl~$%?(%9vk9D-#0EcwJ^w6};dD*cn861^r4qADS+ zor{T8%^*W2von)Xb7gRN7p2$gu=GDz2$gI5@0MY5yXya988TVSw{UW^D7dN71Riae z+{z`_Gk0f=R-Umt(IxO~NQp%_%`HW^W|4}@%`Pg_m>JWRsX^6BSc&-*L3CSO)OgL~ zs=xu$pFCftQa5n#ayrXV=I+Dw~nbI>0aSei;N1z!h=9S&?d%(<^y>#E(i$P#JJFWATP!R0YRG>7n%>`#ke3KXcOZ?^MSk= z7X$=tVq9oGkQd{EfS^r`3(W`eVq6dqw25({`9NNb3j%^RF)lP8$cu47K+q<}h2{f! zF)j!Q+Qhifd>}8z1pz^u7#ErkN z@?u;N5VVPLq4_{wj0*ySHZd+VAIOVwK|s(Z#)aksc`+^s2-?KB(0m{-#svXEn-~|G z59GzTARuTHi7P*I6I7JJ_0M*2&vW~sfo5<=l;mpzK7#1{CP8%f5X9#r;QJCm1f~;t zw>N<6rB4yWJmqZDojbr~UDoTVs*j&Ly|ZE^xRtx*xepcx9xX8i@|QpL%&PD8^t{?P z^`!UiJ*NG$U%Ni1%&~2U_u)sL9ymgIw9Ed$wynjdONx7YjuZ~wx^k|n)Xa6AsC}Z|qPO!d91F#oM&X7uLX=k}5pJ3l=*>%A%e z=)R+Wq@;V7>VJ?v_5 Date: Mon, 23 Apr 2018 06:21:08 +0100 Subject: [PATCH 09/15] Exp trackers plugin redesign Restyled the whole exp trackers plugin panel to follow my proposed design on issue #1342 - Restyled and recolored the exp tracker - Removed the contracted state, trackers are now always expanded as they are now thinner than before - Progress bars are now color matched to the skill using a new enum SkillColor - Added new progresss bar custom component - Added error panel for "no exp gained" that disappears once the player earns experience - Restyled the overall exp panel - Hid the overall exp panel until the player earns experience - Added "Reset" right click menu to individual skill trackers - Added "Reset All" right click menu to the overall skill tracker --- .../client/plugins/xptracker/SkillColor.java | 64 +++++ .../client/plugins/xptracker/XpInfoBox.java | 255 ++++++++---------- .../client/plugins/xptracker/XpPanel.java | 112 +++++--- .../client/ui/components/ProgressBar.java | 116 ++++++++ .../client/plugins/xptracker/reset.png | Bin 4217 -> 0 bytes 5 files changed, 357 insertions(+), 190 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/xptracker/SkillColor.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/xptracker/reset.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/SkillColor.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/SkillColor.java new file mode 100644 index 0000000000..fe2a808e91 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/SkillColor.java @@ -0,0 +1,64 @@ +/* + * 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.plugins.xptracker; + +import java.awt.Color; +import lombok.Getter; + +public enum SkillColor +{ + ATTACK(105, 32, 7), + DEFENCE(98, 119, 190), + STRENGTH(4, 149, 90), + HITPOINTS(131, 126, 126), + RANGED(109, 144, 23), + PRAYER(159, 147, 35), + MAGIC(50, 80, 193), + COOKING(112, 35, 134), + WOODCUTTING(52, 140, 37), + FLETCHING(3, 141, 125), + FISHING(106, 132, 164), + FIREMAKING(189, 120, 25), + CRAFTING(151, 110, 77), + SMITHING(108, 107, 82), + MINING(93, 143, 167), + HERBLORE(7, 133, 9), + AGILITY(58, 60, 137), + THIEVING(108, 52, 87), + SLAYER(100, 100, 100), + FARMING(101, 152, 63), + RUNECRAFT(170, 141, 26), + HUNTER(92, 89, 65), + CONSTRUCTION(130, 116, 95); + + @Getter + private final Color color; + + SkillColor(int red, int green, int blue) + { + this.color = new Color(red, green, blue); + } + +} \ No newline at end of file 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 4ece97d152..547671a335 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 @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Adam + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,54 +27,54 @@ package net.runelite.client.plugins.xptracker; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Dimension; import java.awt.GridLayout; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.image.BufferedImage; -import java.awt.image.LookupOp; -import java.awt.image.LookupTable; import java.io.IOException; import javax.swing.ImageIcon; -import javax.swing.JButton; import javax.swing.JLabel; -import javax.swing.JLayeredPane; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; -import javax.swing.JProgressBar; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; -import javax.swing.border.LineBorder; +import lombok.AccessLevel; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.Skill; import net.runelite.client.game.SkillIconManager; -import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.components.ProgressBar; import net.runelite.client.util.LinkBrowser; -import org.pushingpixels.substance.internal.SubstanceSynapse; +import net.runelite.client.util.StackFormatter; +import net.runelite.client.util.SwingUtil; @Slf4j class XpInfoBox extends JPanel { - private static final Rectangle ICON_BOUNDS = new Rectangle(0, 0, 26, 26); - private final Client client; private final JPanel panel; + + @Getter(AccessLevel.PACKAGE) private final Skill skill; + /* The tracker's wrapping container */ private final JPanel container = new JPanel(); + + /* Contains the skill icon and the stats panel */ + private final JPanel headerPanel = new JPanel(); + + /* Contains all the skill information (exp gained, per hour, etc) */ private final JPanel statsPanel = new JPanel(); - private final JProgressBar progressBar = new JProgressBar(); - private final JLabel xpHr = new JLabel(); - private final JLabel xpGained = new JLabel(); - private final JLabel xpLeft = new JLabel(); + + private final ProgressBar progressBar = new ProgressBar(); + + private final JLabel expGained = new JLabel(); + private final JLabel expHour = new JLabel(); + private final JLabel expLeft = new JLabel(); private final JLabel actionsLeft = new JLabel(); - private final JLabel levelLabel = new JShadowedLabel(); XpInfoBox(XpTrackerPlugin xpTrackerPlugin, Client client, JPanel panel, Skill skill, SkillIconManager iconManager) throws IOException { @@ -82,121 +83,81 @@ class XpInfoBox extends JPanel this.skill = skill; setLayout(new BorderLayout()); - setBorder(new CompoundBorder - ( - new EmptyBorder(2, 0, 2, 0), - new LineBorder(getBackground().brighter(), 1) - )); - - // Expand stats panel on click - Color backgroundColor = getBackground(); - - MouseListener panelMouseListener = new MouseAdapter() - { - @Override - public void mouseEntered(MouseEvent e) - { - container.setBackground(backgroundColor.darker().darker()); - } - @Override - public void mouseExited(MouseEvent e) - { - container.setBackground(backgroundColor); - } - @Override - public void mouseReleased(MouseEvent e) - { - if (SwingUtilities.isLeftMouseButton(e)) - { - showStatsPanel(); - } - } - }; + setBorder(new EmptyBorder(10, 0, 0, 0)); + setOpaque(false); container.setLayout(new BorderLayout()); - container.setBorder(new EmptyBorder(5, 5, 5, 5)); - container.addMouseListener(panelMouseListener); + container.setOpaque(true); + container.setBackground(ColorScheme.DARKER_GRAY_COLOR); // Create open xp tracker menu - final JMenuItem openXpTracker = new JMenuItem("Open XP tracker"); + final JMenuItem openXpTracker = new JMenuItem("Open online tracker"); openXpTracker.addActionListener(e -> LinkBrowser.browse(XpPanel.buildXpTrackerUrl(client.getLocalPlayer(), skill))); + // Create reset menu + final JMenuItem reset = new JMenuItem("Reset"); + reset.addActionListener(e -> reset()); + // Create popup menu final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); popupMenu.add(openXpTracker); + popupMenu.add(reset); container.setComponentPopupMenu(popupMenu); - // Create icon panel - final JPanel iconBarPanel = new JPanel(); - iconBarPanel.setLayout(new BorderLayout(5, 0)); - iconBarPanel.setOpaque(false); + JLabel skillIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(skill))); + skillIcon.setHorizontalAlignment(SwingConstants.CENTER); + skillIcon.setVerticalAlignment(SwingConstants.CENTER); + skillIcon.setPreferredSize(new Dimension(35, 35)); - // Create skill/reset icon - final BufferedImage skillImage = iconManager.getSkillImage(skill); - final JButton skillIcon = new JButton(); + headerPanel.setOpaque(false); + headerPanel.setLayout(new BorderLayout()); - skillIcon.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE); - skillIcon.putClientProperty(SubstanceSynapse.BUTTON_NEVER_PAINT_BACKGROUND, Boolean.TRUE); - skillIcon.setIcon(new ImageIcon(skillImage)); - skillIcon.setRolloverIcon(new ImageIcon(createHoverImage(skillImage))); - - skillIcon.setToolTipText("Reset " + skill.getName() + " tracker"); - skillIcon.addActionListener(e -> xpTrackerPlugin.resetSkillState(skill)); - skillIcon.setBounds(ICON_BOUNDS); - skillIcon.setOpaque(false); - skillIcon.setFocusPainted(false); - - // Create level label - levelLabel.setHorizontalAlignment(JLabel.CENTER); - levelLabel.setForeground(Color.YELLOW); - levelLabel.setBounds(ICON_BOUNDS); - levelLabel.setOpaque(false); - - // Create pane for grouping skill icon and level label - final JLayeredPane layeredPane = new JLayeredPane(); - layeredPane.add(skillIcon, new Integer(1)); - layeredPane.add(levelLabel, new Integer(2)); - layeredPane.setPreferredSize(ICON_BOUNDS.getSize()); - iconBarPanel.add(layeredPane, BorderLayout.LINE_START); - - // Create progress bar - progressBar.setStringPainted(true); - progressBar.setValue(0); - progressBar.setMinimum(0); - progressBar.setMaximum(100); - progressBar.setBackground(Color.RED); - progressBar.addMouseListener(panelMouseListener); - iconBarPanel.add(progressBar, BorderLayout.CENTER); - - container.add(iconBarPanel, BorderLayout.NORTH); - - // Stats panel - statsPanel.setLayout(new GridLayout(2, 2)); - statsPanel.setBorder(new EmptyBorder(3, 0, 0, 0)); + statsPanel.setLayout(new BorderLayout()); + statsPanel.setBorder(new EmptyBorder(9, 5, 9, 10)); statsPanel.setOpaque(false); - statsPanel.add(xpGained); - xpHr.setHorizontalAlignment(SwingConstants.RIGHT); - statsPanel.add(xpHr); - statsPanel.add(xpLeft); - actionsLeft.setHorizontalAlignment(SwingConstants.RIGHT); - statsPanel.add(actionsLeft); + JPanel leftPanel = new JPanel(); + leftPanel.setOpaque(false); + leftPanel.setLayout(new GridLayout(2, 1)); - add(container, BorderLayout.CENTER); - } + expGained.setFont(FontManager.getRunescapeSmallFont()); + expHour.setFont(FontManager.getRunescapeSmallFont()); - private void showStatsPanel() - { - if (statsPanel.isShowing()) - { - container.remove(statsPanel); - revalidate(); - } - else - { - container.add(statsPanel, BorderLayout.SOUTH); - revalidate(); - } + leftPanel.add(expGained); + leftPanel.add(expHour); + + JPanel rightPanel = new JPanel(); + rightPanel.setOpaque(false); + rightPanel.setLayout(new GridLayout(2, 1)); + + expLeft.setFont(FontManager.getRunescapeSmallFont()); + actionsLeft.setFont(FontManager.getRunescapeSmallFont()); + + rightPanel.add(expLeft); + rightPanel.add(actionsLeft); + + statsPanel.add(leftPanel, BorderLayout.WEST); + statsPanel.add(rightPanel, BorderLayout.EAST); + + headerPanel.add(skillIcon, BorderLayout.WEST); + headerPanel.add(statsPanel, BorderLayout.CENTER); + + JPanel progressWrapper = new JPanel(); + progressWrapper.setOpaque(false); + progressWrapper.setLayout(new BorderLayout()); + progressWrapper.setBorder(new EmptyBorder(0, 7, 7, 7)); + + progressBar.setMaximumValue(100); + progressBar.setBackground(new Color(61, 56, 49)); + progressBar.setForeground(SkillColor.values()[skill.ordinal()].getColor()); + + progressWrapper.add(progressBar, BorderLayout.NORTH); + + container.add(headerPanel, BorderLayout.NORTH); + container.add(progressWrapper, BorderLayout.SOUTH); + + add(container, BorderLayout.NORTH); } void reset() @@ -221,46 +182,46 @@ class XpInfoBox extends JPanel panel.revalidate(); } - levelLabel.setText(String.valueOf(xpSnapshotSingle.getCurrentLevel())); - xpGained.setText(XpPanel.formatLine(xpSnapshotSingle.getXpGainedInSession(), "xp gained")); - xpLeft.setText(XpPanel.formatLine(xpSnapshotSingle.getXpRemainingToGoal(), "xp left")); - actionsLeft.setText(XpPanel.formatLine(xpSnapshotSingle.getActionsRemainingToGoal(), "actions left")); + // Update information labels + expGained.setText(htmlLabel("XP Gained: ", xpSnapshotSingle.getXpGainedInSession())); + expLeft.setText(htmlLabel("XP Left: ", xpSnapshotSingle.getXpRemainingToGoal())); + actionsLeft.setText(htmlLabel("Actions: ", xpSnapshotSingle.getActionsRemainingToGoal())); - final int progress = xpSnapshotSingle.getSkillProgressToGoal(); - - progressBar.setValue(progress); - progressBar.setBackground(Color.getHSBColor((progress / 100.f) * (120.f / 360.f), 1, 1)); + // Update progress bar + progressBar.setValue(xpSnapshotSingle.getSkillProgressToGoal()); + progressBar.setCenterLabel(xpSnapshotSingle.getSkillProgressToGoal() + "%"); + progressBar.setLeftLabel("Lvl. " + xpSnapshotSingle.getCurrentLevel()); + progressBar.setRightLabel("Lvl. " + (xpSnapshotSingle.getCurrentLevel() + 1)); progressBar.setToolTipText("" - + XpPanel.formatLine(xpSnapshotSingle.getActionsInSession(), "actions") + + xpSnapshotSingle.getActionsInSession() + " actions done" + "
" - + XpPanel.formatLine(xpSnapshotSingle.getActionsPerHour(), "actions/hr") + + xpSnapshotSingle.getActionsPerHour() + " actions/hr" + "
" + xpSnapshotSingle.getTimeTillGoal() + " till next lvl" + ""); + + progressBar.repaint(); } - // Always update xp/hr as time always changes - xpHr.setText(XpPanel.formatLine(xpSnapshotSingle.getXpPerHour(), "xp/hr")); + // Update exp per hour seperately, everytime (not only when there's an update) + expHour.setText(htmlLabel("XP/Hour: ", xpSnapshotSingle.getXpPerHour())); } - private static BufferedImage createHoverImage(BufferedImage image) + public static String htmlLabel(String key, int value) { - LookupTable lookup = new LookupTable(0, 4) + String valueStr = value + ""; + + if (value > 9999999 || value < -9999999) { - @Override - public int[] lookupPixel(int[] src, int[] dest) - { - if (dest[3] != 0) - { - dest[3] = 60; - } + valueStr = "Lots!"; + } + else + { + valueStr = StackFormatter.quantityToRSDecimalStack(value); + } - return dest; - } - }; - - LookupOp op = new LookupOp(lookup, new RenderingHints(null)); - return op.filter(image, null); + return "" + key + "" + valueStr + ""; } + } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java index 1782caba72..602ab9a3d2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Cameron + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,64 +30,93 @@ import java.awt.GridLayout; import java.io.IOException; import java.util.HashMap; import java.util.Map; -import javax.swing.BorderFactory; import javax.swing.BoxLayout; -import javax.swing.JButton; +import javax.swing.ImageIcon; import javax.swing.JLabel; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Actor; import net.runelite.api.Client; import net.runelite.api.Skill; import net.runelite.client.game.SkillIconManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.PluginErrorPanel; import net.runelite.client.util.LinkBrowser; -import net.runelite.client.util.StackFormatter; import okhttp3.HttpUrl; @Slf4j class XpPanel extends PluginPanel { private final Map infoBoxes = new HashMap<>(); - private final JLabel totalXpGained = new JLabel(); - private final JLabel totalXpHr = new JLabel(); + + private final JLabel overallExpGained = new JLabel(XpInfoBox.htmlLabel("Gained: ", 0)); + private final JLabel overallExpHour = new JLabel(XpInfoBox.htmlLabel("Per hour: ", 0)); + + private final JPanel overallPanel = new JPanel(); + + /* This displays the "No exp gained" text */ + private final PluginErrorPanel errorPanel = new PluginErrorPanel(); XpPanel(XpTrackerPlugin xpTrackerPlugin, Client client, SkillIconManager iconManager) { super(); + setBorder(new EmptyBorder(10, 10, 10, 10)); + setBackground(ColorScheme.DARK_GRAY_COLOR); + final JPanel layoutPanel = new JPanel(); - layoutPanel.setLayout(new BorderLayout(0, 3)); + layoutPanel.setOpaque(false); + layoutPanel.setLayout(new BorderLayout()); add(layoutPanel); - final JPanel totalPanel = new JPanel(); - totalPanel.setLayout(new BorderLayout()); - totalPanel.setBorder(BorderFactory.createLineBorder(getBackground().brighter(), 1, true)); + overallPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + overallPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + overallPanel.setLayout(new BorderLayout()); + overallPanel.setVisible(false); // this will only become visible when the player gets exp - final JPanel infoPanel = new JPanel(); - infoPanel.setLayout(new GridLayout(4, 1)); - infoPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); + // Create open xp tracker menu + final JMenuItem openXpTracker = new JMenuItem("Open online tracker"); + openXpTracker.addActionListener(e -> LinkBrowser.browse(XpPanel.buildXpTrackerUrl(client.getLocalPlayer(), Skill.OVERALL))); - final JButton resetButton = new JButton("Reset All"); - resetButton.addActionListener(e -> xpTrackerPlugin.resetAndInitState()); + // Create reset all menu + final JMenuItem reset = new JMenuItem("Reset All"); + reset.addActionListener(e -> xpTrackerPlugin.resetAndInitState()); - final JButton openTrackerButton = new JButton("Open XP tracker"); - openTrackerButton.addActionListener(e -> LinkBrowser.browse(buildXpTrackerUrl(client.getLocalPlayer(), Skill.OVERALL))); + // Create popup menu + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + popupMenu.add(openXpTracker); + popupMenu.add(reset); + overallPanel.setComponentPopupMenu(popupMenu); - totalXpGained.setText(formatLine(0, "total xp gained")); - totalXpHr.setText(formatLine(0, "total xp/hr")); + final JLabel overallIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(Skill.OVERALL))); + + final JPanel overallInfo = new JPanel(); + overallInfo.setOpaque(false); + overallInfo.setLayout(new GridLayout(2, 1)); + overallInfo.setBorder(new EmptyBorder(0, 10, 0, 0)); + + overallExpGained.setFont(FontManager.getRunescapeSmallFont()); + overallExpHour.setFont(FontManager.getRunescapeSmallFont()); + + overallInfo.add(overallExpGained); + overallInfo.add(overallExpHour); + + overallPanel.add(overallIcon, BorderLayout.WEST); + overallPanel.add(overallInfo, BorderLayout.CENTER); - infoPanel.add(totalXpGained); - infoPanel.add(totalXpHr); - infoPanel.add(resetButton); - infoPanel.add(openTrackerButton); - totalPanel.add(infoPanel, BorderLayout.CENTER); - layoutPanel.add(totalPanel, BorderLayout.NORTH); final JPanel infoBoxPanel = new JPanel(); + infoBoxPanel.setOpaque(false); infoBoxPanel.setLayout(new BoxLayout(infoBoxPanel, BoxLayout.Y_AXIS)); layoutPanel.add(infoBoxPanel, BorderLayout.CENTER); + layoutPanel.add(overallPanel, BorderLayout.NORTH); try { @@ -96,7 +126,6 @@ class XpPanel extends PluginPanel { break; } - infoBoxes.put(skill, new XpInfoBox(xpTrackerPlugin, client, infoBoxPanel, skill, iconManager)); } } @@ -104,6 +133,10 @@ class XpPanel extends PluginPanel { log.warn(null, e); } + + errorPanel.setContent("Exp trackers", "You have not gained experience yet."); + errorPanel.setBorder(new EmptyBorder(0, 0, 0, 0)); + add(errorPanel); } static String buildXpTrackerUrl(final Actor player, final Skill skill) @@ -150,30 +183,23 @@ class XpPanel extends PluginPanel } } - void updateTotal(XpSnapshotTotal xpSnapshotTotal) + + public void updateTotal(XpSnapshotTotal xpSnapshotTotal) { + // if player has gained exp and hasn't switched displays yet, hide error panel and show overall info + if (xpSnapshotTotal.getXpGainedInSession() > 0 && !overallPanel.isVisible()) + { + overallPanel.setVisible(true); + remove(errorPanel); + } + SwingUtilities.invokeLater(() -> rebuildAsync(xpSnapshotTotal)); } private void rebuildAsync(XpSnapshotTotal xpSnapshotTotal) { - totalXpGained.setText(formatLine(xpSnapshotTotal.getXpGainedInSession(), "total xp gained")); - totalXpHr.setText(formatLine(xpSnapshotTotal.getXpPerHour(), "total xp/hr")); + overallExpGained.setText(XpInfoBox.htmlLabel("Gained: ", xpSnapshotTotal.getXpGainedInSession())); + overallExpHour.setText(XpInfoBox.htmlLabel("Per hour: ", xpSnapshotTotal.getXpPerHour())); } - static String formatLine(double number, String description) - { - String numberStr; - if (number < 100000) - { - numberStr = StackFormatter.formatNumber(number); - } - else - { - int num = (int) (Math.log(number) / Math.log(1000)); - numberStr = String.format("%.1f%c", number / Math.pow(1000, num), "KMB".charAt(num - 1)); - } - - return numberStr + " " + description; - } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java new file mode 100644 index 0000000000..c94845ee1e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java @@ -0,0 +1,116 @@ +/* + * 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.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; +import lombok.Setter; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; + +/** + * A progress bar to be displayed underneath the GE offer item panels + */ +public class ProgressBar extends JPanel +{ + @Setter + private int maximumValue; + + @Setter + private int value; + + private final JLabel leftLabel = new JShadowedLabel(); + private final JLabel rightLabel = new JShadowedLabel(); + private final JLabel centerLabel = new JShadowedLabel(); + + public ProgressBar() + { + setLayout(new BorderLayout()); + setBackground(Color.GREEN.darker()); + setPreferredSize(new Dimension(100, 16)); + + leftLabel.setFont(FontManager.getRunescapeSmallFont()); + leftLabel.setForeground(Color.WHITE); + leftLabel.setBorder(new EmptyBorder(2, 5, 0, 0)); + + rightLabel.setFont(FontManager.getRunescapeSmallFont()); + rightLabel.setForeground(Color.WHITE); + rightLabel.setBorder(new EmptyBorder(2, 0, 0, 5)); + + centerLabel.setFont(FontManager.getRunescapeSmallFont()); + centerLabel.setForeground(Color.WHITE); + centerLabel.setHorizontalAlignment(SwingConstants.CENTER); + centerLabel.setBorder(new EmptyBorder(2, 0, 0, 0)); + + add(leftLabel, BorderLayout.WEST); + add(centerLabel, BorderLayout.CENTER); + add(rightLabel, BorderLayout.EAST); + + } + + @Override + public void paint(Graphics g) + { + double percentage = getPercentage(); + int topWidth = (int) (getSize().width * (percentage / 100)); + + super.paint(g); + g.setColor(getForeground()); + g.fillRect(0, 0, topWidth, 16); + super.paintComponents(g); + } + + + public void setLeftLabel(String txt) + { + this.leftLabel.setText(txt); + } + + public void setRightLabel(String txt) + { + this.rightLabel.setText(txt); + } + + public void setCenterLabel(String txt) + { + this.centerLabel.setText(txt); + } + + public double getPercentage() + { + if (value == 0) + { + return 0; + } + + return (value * 100) / maximumValue; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/xptracker/reset.png b/runelite-client/src/main/resources/net/runelite/client/plugins/xptracker/reset.png deleted file mode 100644 index 4d9d11103798889ab9c86b59c83e1846caedede3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4217 zcmeHJKS&!<7{ANk^SHZQf&p_O;FKXkDMpN2R4z1;A|f3sT{;zn4grT0anh+nDIFYi zaVixA!No!+2RD_vWe^E&mLer25DwAw_XQtZNbXN-Cb@%m@4N5)zCZ8xzV~q-7v^8c zq9$@2Czp!)BIZ#VLJ04b)zv1Zp>lC)h2xZC8vN#%lH@pbtfbGpdOgICsq`1jiNN6p3h49ssBLzFW4GTVNh%_c28KptS=NEu;d%a!Fks{)kw^?C z2?X0BcZ*%pnp{&fzRZS4FtGB64O?GPzMuW;+F78$P&lw zeN3>L;jBX?=6g_kj-uOz94nX0eQoRY`YDp$CV#C~YuU=LYdwnaC7m~lP<3k*jYjD< zw2|y@w>Gx8OeP~03Wb0z$h4TvW<{{Xj?J{vD>^#G75rJPR-ZK*4g5=R*S#)!HG&}A z_iF!B0~%{ErRvw+x`@SM<75L{$3LFFg=7wdsz0J{!!VZ2dONwNN5vRZ8De33Hf|pfIJS}C$@8(eKj7B4|h3v><9JshxW>@7bVjflo%Vd z^MMC9CyyhTMUXx0@`eSaYKQKC&U|3Bn}d3?$AHJclV-qJU;D8 Date: Tue, 24 Apr 2018 05:05:25 +0100 Subject: [PATCH 10/15] Info panel redesign Restyled the whole exp trackers plugin panel to follow my proposed design style on issue #1342 - Added two new entries to the runelite properties file (github and patreon link). And addes respective getter methods in the RuneLiteProperties.java file. --- .../runelite/client/RuneLiteProperties.java | 14 +- .../client/plugins/info/InfoPanel.java | 190 ++++++++++++------ .../client/plugins/info/arrow_right.png | Bin 0 -> 122 bytes .../client/plugins/info/discord_icon.png | Bin 0 -> 16013 bytes .../client/plugins/info/github_icon.png | Bin 0 -> 16063 bytes .../client/plugins/info/patreon_icon.png | Bin 0 -> 15911 bytes .../net/runelite/client/runelite.properties | 4 +- 7 files changed, 147 insertions(+), 61 deletions(-) create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/info/arrow_right.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/info/discord_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/info/github_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/info/patreon_icon.png diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java index 6d840f754b..c8168b878c 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java @@ -41,6 +41,8 @@ public class RuneLiteProperties private static final String RUNESCAPE_VERSION = "runescape.version"; private static final String DISCORD_APP_ID = "runelite.discord.appid"; private static final String DISCORD_INVITE = "runelite.discord.invite"; + private static final String GITHUB_LINK = "runelite.github.link"; + private static final String PATREON_LINK = "runelite.patreon.link"; private final Properties properties = new Properties(); @@ -82,4 +84,14 @@ public class RuneLiteProperties { return properties.getProperty(DISCORD_INVITE); } -} + + public String getGithubLink() + { + return properties.getProperty(GITHUB_LINK); + } + + public String getPatreonLink() + { + return properties.getProperty(PATREON_LINK); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java index 42de837fa5..778c3e262b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Abex + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,14 +28,22 @@ package net.runelite.client.plugins.info; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; +import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Font; +import java.awt.GridLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.IOException; import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; +import javax.imageio.ImageIO; import javax.inject.Singleton; -import javax.swing.BorderFactory; -import javax.swing.GroupLayout; +import javax.swing.Box; +import javax.swing.ImageIcon; import javax.swing.JLabel; -import javax.swing.LayoutStyle; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; import javax.swing.event.HyperlinkEvent; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; @@ -42,8 +51,10 @@ import net.runelite.api.events.SessionClose; import net.runelite.api.events.SessionOpen; import net.runelite.client.RuneLiteProperties; import net.runelite.client.account.SessionManager; +import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.PluginPanel; +import net.runelite.client.util.LinkBrowser; import net.runelite.client.util.RunnableExceptionLogger; @Slf4j @@ -52,6 +63,14 @@ public class InfoPanel extends PluginPanel { private static final String RUNELITE_LOGIN = "https://runelite_login/"; + private static final ImageIcon ARROW_RIGHT_ICON; + private static final ImageIcon GITHUB_ICON; + private static final ImageIcon DISCORD_ICON; + private static final ImageIcon PATREON_ICON; + + private final JLabel loggedLabel = new JLabel(); + private final JRichTextPane emailLabel = new JRichTextPane(); + @Inject @Nullable private Client client; @@ -68,33 +87,58 @@ public class InfoPanel extends PluginPanel @Inject private ScheduledExecutorService executor; - private final GroupLayout layout = new GroupLayout(this); - private final JLabel usernameHeader = new JLabel(); - private final JRichTextPane username = new JRichTextPane(); + static + { + try + { + synchronized (ImageIO.class) + { + ARROW_RIGHT_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("arrow_right.png"))); + GITHUB_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("github_icon.png"))); + DISCORD_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("discord_icon.png"))); + PATREON_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("patreon_icon.png"))); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } void init() { - setLayout(layout); + setLayout(new BorderLayout()); + setBackground(ColorScheme.DARK_GRAY_COLOR); + setBorder(new EmptyBorder(10, 10, 10, 10)); + + JPanel versionPanel = new JPanel(); + versionPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + versionPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + versionPanel.setLayout(new GridLayout(0, 1)); final Font smallFont = FontManager.getRunescapeSmallFont(); - final JLabel runeliteVersionHeader = new JLabel("RuneLite version"); - runeliteVersionHeader.setFont(smallFont); - final JLabel runeliteVersion = new JLabel(runeLiteProperties.getVersion()); + JLabel version = new JLabel(htmlLabel("RuneLite version: ", runeLiteProperties.getVersion())); + version.setFont(smallFont); - final JLabel runescapeVersionHeader = new JLabel("OldSchool Engine"); - runescapeVersionHeader.setFont(smallFont); + JLabel revision = new JLabel(); + revision.setFont(smallFont); String engineVer = "Unknown"; if (client != null) { engineVer = String.format("Rev %s", runeLiteProperties.getRunescapeVersion()); } - final JLabel runescapeVersion = new JLabel(engineVer); - usernameHeader.setFont(smallFont); - username.enableAutoLinkHandler(false); - username.addHyperlinkListener(e -> + revision.setText(htmlLabel("Oldschool revision: ", engineVer)); + + loggedLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR); + loggedLabel.setFont(smallFont); + + emailLabel.setForeground(Color.WHITE); + emailLabel.setFont(smallFont); + emailLabel.enableAutoLinkHandler(false); + emailLabel.addHyperlinkListener(e -> { if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType()) && e.getURL() != null) { @@ -105,51 +149,74 @@ public class InfoPanel extends PluginPanel } }); + versionPanel.add(version); + versionPanel.add(revision); + versionPanel.add(Box.createGlue()); + versionPanel.add(loggedLabel); + versionPanel.add(emailLabel); + updateLoggedIn(); - final JRichTextPane issueLink = new JRichTextPane("text/html", - "RuneLite is open source!
" - + "Found an issue? Want a feature?
" - + "" - + "Open an issue on GitHub!" - + "" - ); + JPanel actionsContainer = new JPanel(); + actionsContainer.setBorder(new EmptyBorder(10, 0, 0, 0)); + actionsContainer.setOpaque(false); + actionsContainer.setLayout(new GridLayout(3, 1, 0, 10)); - setBorder(BorderFactory.createEmptyBorder(2, 6, 6, 6)); + actionsContainer.add(buildLinkPanel(GITHUB_ICON, "Report an issue or", "make a suggestion", runeLiteProperties.getGithubLink())); + actionsContainer.add(buildLinkPanel(DISCORD_ICON, "Talk to us on our", "discord server", runeLiteProperties.getDiscordInvite())); + actionsContainer.add(buildLinkPanel(PATREON_ICON, "Become a patron to", "help support RuneLite", runeLiteProperties.getPatreonLink())); - layout.setVerticalGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup() - .addComponent(runeliteVersionHeader) - .addComponent(runescapeVersionHeader) - ).addGroup(layout.createParallelGroup() - .addComponent(runeliteVersion) - .addComponent(runescapeVersion) - ).addGap(6) - .addComponent(usernameHeader) - .addGroup(layout.createParallelGroup() - .addComponent(username) - ) - .addGap(12) - .addComponent(issueLink) - ); - - layout.setHorizontalGroup(layout.createParallelGroup() - .addGroup(layout.createSequentialGroup() - .addComponent(runeliteVersionHeader) - .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) - .addComponent(runescapeVersionHeader)) - .addGroup(layout.createSequentialGroup() - .addComponent(runeliteVersion) - .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) - .addComponent(runescapeVersion)) - .addComponent(usernameHeader) - .addComponent(username) - .addComponent(issueLink) - ); + add(versionPanel, BorderLayout.NORTH); + add(actionsContainer, BorderLayout.CENTER); eventBus.register(this); } + /** + * Builds a link panel with a given icon, text and url to redirect to. + */ + private static JPanel buildLinkPanel(ImageIcon icon, String topText, String bottomText, String url) + { + JPanel container = new JPanel(); + container.setBackground(ColorScheme.DARKER_GRAY_COLOR); + container.setLayout(new BorderLayout()); + container.setBorder(new EmptyBorder(10, 10, 10, 10)); + container.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + LinkBrowser.browse(url); + } + }); + + JLabel iconLabel = new JLabel(icon); + container.add(iconLabel, BorderLayout.WEST); + + JPanel textContainer = new JPanel(); + textContainer.setOpaque(false); + textContainer.setLayout(new GridLayout(2, 1)); + textContainer.setBorder(new EmptyBorder(5, 10, 5, 10)); + + JLabel topLine = new JLabel(topText); + topLine.setForeground(Color.WHITE); + topLine.setFont(FontManager.getRunescapeSmallFont()); + + JLabel bottomLine = new JLabel(bottomText); + bottomLine.setForeground(Color.WHITE); + bottomLine.setFont(FontManager.getRunescapeSmallFont()); + + textContainer.add(topLine); + textContainer.add(bottomLine); + + container.add(textContainer, BorderLayout.CENTER); + + JLabel arrowLabel = new JLabel(ARROW_RIGHT_ICON); + container.add(arrowLabel, BorderLayout.EAST); + + return container; + } + private void updateLoggedIn() { final String name = sessionManager.getAccountSession() != null @@ -158,18 +225,23 @@ public class InfoPanel extends PluginPanel if (name != null) { - username.setContentType("text/plain"); - username.setText(name); - usernameHeader.setText("Logged in as"); + emailLabel.setContentType("text/plain"); + emailLabel.setText(name); + loggedLabel.setText("Logged in as"); } else { - username.setContentType("text/html"); - username.setText("Login to sync settings to the cloud."); - usernameHeader.setText("Not logged in"); + emailLabel.setContentType("text/html"); + emailLabel.setText("Login to sync settings to the cloud."); + loggedLabel.setText("Not logged in"); } } + private static String htmlLabel(String key, String value) + { + return "" + key + "" + value + ""; + } + @Subscribe public void onSessionOpen(SessionOpen sessionOpen) { diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/info/arrow_right.png b/runelite-client/src/main/resources/net/runelite/client/plugins/info/arrow_right.png new file mode 100644 index 0000000000000000000000000000000000000000..92048f0c7844249c635de465efc0b258c64ccd61 GIT binary patch literal 122 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|m96eneLp07OCrCVbV1N7nq5o@I zM1KDH|G%C$F}3oDrnZ7#q-wy;qCZ|0|K)%Fe_pR~b}EDD&VT>?95^FpsvH(zWC%QE VbfiC~pbBUhgQu&X%Q~loCIGE8ElvOc literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/info/discord_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/info/discord_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a5d1d5722e0365a5e429fa34940f1a8a0ada976b GIT binary patch literal 16013 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 Dz9Z8M literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/runelite.properties b/runelite-client/src/main/resources/net/runelite/client/runelite.properties index 4860325109..fa6ece9990 100644 --- a/runelite-client/src/main/resources/net/runelite/client/runelite.properties +++ b/runelite-client/src/main/resources/net/runelite/client/runelite.properties @@ -2,4 +2,6 @@ runelite.title=RuneLite runelite.version=${project.version} runescape.version=${rs.version} runelite.discord.appid=409416265891971072 -runelite.discord.invite=https://discord.gg/R4BQ8tU \ No newline at end of file +runelite.discord.invite=https://discord.gg/R4BQ8tU +runelite.github.link=https://github.com/runelite +runelite.patreon.link=https://www.patreon.com/runelite \ No newline at end of file From 6d331ae5a1ba432141bdda57b9950d31a6d458df Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Tue, 1 May 2018 04:50:17 +0100 Subject: [PATCH 11/15] Farming plugin panel redesign Farming Plugin: - Redid the layout, coloring and positioning on the FarmingTrackerPanel to include my new MaterialTabs. - Added section divider labels in the special and tree tabs. - Hid the patch type indicator for some patches (explained in docs). - Added patch names for all instances of FarmingPatch (previously only a portion of them were written for a hack that is no longer necessary). - Created getName() method in PatchImplementation that returns the patch type's name in a formatted style (SPIRIT_TREE -> Spirit Tree). - Created a new tab for bushes. MaterialTabs: - Added onSelectEvent runnable to MaterialTab. - Added horizontal gap to the tabs. - Added getTab() method that returns a tab on a given index. CustomScrollBarUI: - Added functionality to allow the ui colors to be changed from an external call. (setters) ColorScheme: - Created color constants in the ColorScheme file for the progresss bar backgrounds and used them in the CropState enum. --- .../plugins/farmingtracker/CropState.java | 9 +- .../farmingtracker/FarmingPatchPanel.java | 93 +++++++++----- .../farmingtracker/FarmingTrackerPanel.java | 113 ++++++++++++++---- .../plugins/farmingtracker/FarmingWorld.java | 44 +++---- .../farmingtracker/PatchImplementation.java | 15 ++- .../client/plugins/farmingtracker/Tab.java | 1 + .../ui/components/CustomScrollBarUI.java | 15 ++- .../components/materialtabs/MaterialTab.java | 9 ++ .../materialtabs/MaterialTabGroup.java | 15 ++- 9 files changed, 229 insertions(+), 85 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/CropState.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/CropState.java index edfc15be1e..90bd66ff73 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/CropState.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/CropState.java @@ -27,15 +27,16 @@ package net.runelite.client.plugins.farmingtracker; import java.awt.Color; import lombok.Getter; import lombok.RequiredArgsConstructor; +import net.runelite.client.ui.ColorScheme; @RequiredArgsConstructor @Getter public enum CropState { - HARVESTABLE(Color.GREEN), - GROWING(Color.GREEN), - DISEASED(Color.ORANGE), - DEAD(Color.RED); + HARVESTABLE(ColorScheme.PROGRESS_COMPLETE_COLOR), + GROWING(ColorScheme.PROGRESS_COMPLETE_COLOR), + DISEASED(ColorScheme.PROGRESS_INPROGRESS_COLOR), + DEAD(ColorScheme.PROGRESS_ERROR_COLOR); private final Color color; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingPatchPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingPatchPanel.java index c548236a6e..f2fc12b7a6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingPatchPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingPatchPanel.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Abex + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,13 +25,18 @@ */ package net.runelite.client.plugins.farmingtracker; +import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Dimension; -import javax.swing.GroupLayout; +import java.awt.GridLayout; import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.JProgressBar; +import javax.swing.border.EmptyBorder; import lombok.Getter; +import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.components.ThinProgressBar; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; @Getter class FarmingPatchPanel extends JPanel @@ -38,42 +44,69 @@ class FarmingPatchPanel extends JPanel private final FarmingPatch patch; private final JLabel icon = new JLabel(); private final JLabel estimate = new JLabel(); - private final JProgressBar progress = new JProgressBar(); + private final ThinProgressBar progress = new ThinProgressBar(); FarmingPatchPanel(FarmingPatch patch) { this.patch = patch; - GroupLayout layout = new GroupLayout(this); - this.setLayout(layout); + setLayout(new BorderLayout()); + setOpaque(false); + setBorder(new EmptyBorder(7, 0, 0, 0)); + + JPanel topContainer = new JPanel(); + topContainer.setBorder(new EmptyBorder(7, 7, 6, 0)); + topContainer.setLayout(new BorderLayout()); + topContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); - final JLabel location = new JLabel(patch.getRegion().getName() + " " + patch.getName()); - location.setFont(FontManager.getRunescapeSmallFont()); icon.setMinimumSize(new Dimension(36, 32)); - layout.setVerticalGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER) - .addComponent(icon) - .addGroup(layout.createSequentialGroup() - .addGap(1) - .addComponent(location) - .addGap(1) - .addComponent(estimate) - ) - ) - .addComponent(progress, 8, 8, 8) - .addGap(4) - ); + JPanel infoPanel = new JPanel(); + infoPanel.setOpaque(false); + infoPanel.setLayout(new GridLayout(2, 1)); + infoPanel.setBorder(new EmptyBorder(4, 4, 4, 0)); - layout.setHorizontalGroup(layout.createParallelGroup() - .addGroup(layout.createSequentialGroup() - .addComponent(icon) - .addGroup(layout.createParallelGroup() - .addComponent(location) - .addComponent(estimate) - ) - ) - .addComponent(progress) - ); + final JLabel location = new JShadowedLabel(patch.getRegion().getName() + + (showFullTitle() ? " (" + patch.getName() + ")" : "")); + location.setFont(FontManager.getRunescapeSmallFont()); + location.setForeground(Color.WHITE); + + estimate.setFont(FontManager.getRunescapeSmallFont()); + estimate.setForeground(Color.GRAY); + + infoPanel.add(location); + infoPanel.add(estimate); + + topContainer.add(icon, BorderLayout.WEST); + topContainer.add(infoPanel, BorderLayout.CENTER); + + add(topContainer, BorderLayout.NORTH); + add(progress, BorderLayout.SOUTH); + } + + /** + * This determines if the label should display location and type, ex: + * It makes sense to display: + * Catherby (North) <-- for allotment + * but not so much for herbs: + * Troll Stronghold + * as there are no other herb patches in that region. + */ + private boolean showFullTitle() + { + switch (patch.getImplementation()) + { + case FLOWER: + case HOPS: + case BUSH: + case FRUIT_TREE: + case CALQUAT: + case SPIRIT_TREE: + case TREE: + case HERB: + return false; + default: + return true; + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPanel.java index 4968999ecb..77eed36352 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPanel.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Abex + * Copyright (c) 2018, Psikoi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +30,7 @@ import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; +import java.awt.image.BufferedImage; import java.time.Instant; import java.time.LocalDateTime; import java.time.OffsetDateTime; @@ -37,9 +39,10 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; import javax.swing.ImageIcon; +import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; -import javax.swing.JTabbedPane; +import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; @@ -47,7 +50,11 @@ import net.runelite.api.vars.Autoweed; import net.runelite.client.config.ConfigManager; import net.runelite.client.game.AsyncBufferedImage; import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.materialtabs.MaterialTab; +import net.runelite.client.ui.components.materialtabs.MaterialTabGroup; @Slf4j class FarmingTrackerPanel extends PluginPanel @@ -61,6 +68,10 @@ class FarmingTrackerPanel extends PluginPanel private List patchPanels = new ArrayList<>(); + /* This is the panel the tabs' respective panels will be displayed on. */ + private final JPanel display = new JPanel(); + private final MaterialTabGroup tabGroup = new MaterialTabGroup(display); + FarmingTrackerPanel( Client client, ItemManager itemManager, @@ -77,11 +88,19 @@ class FarmingTrackerPanel extends PluginPanel this.config = config; setLayout(new BorderLayout()); + setBackground(ColorScheme.DARK_GRAY_COLOR); + + display.setOpaque(false); + display.setBorder(new EmptyBorder(10, 10, 8, 10)); + + tabGroup.setBorder(new EmptyBorder(10, 1, 0, 0)); + + add(tabGroup, BorderLayout.NORTH); + add(display, BorderLayout.CENTER); - JTabbedPane tabs = new JTabbedPane(); farmingWorld.getTabs().forEach((tab, patches) -> { - JPanel panel = new JPanel(new GridBagLayout()) + JPanel container = new JPanel(new GridBagLayout()) { @Override public Dimension getPreferredSize() @@ -89,7 +108,7 @@ class FarmingTrackerPanel extends PluginPanel return new Dimension(PluginPanel.PANEL_WIDTH, super.getPreferredSize().height); } }; - panel.setBorder(new EmptyBorder(2, 6, 6, 6)); + container.setBackground(ColorScheme.DARK_GRAY_COLOR); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; @@ -97,43 +116,77 @@ class FarmingTrackerPanel extends PluginPanel c.gridx = 0; c.gridy = 0; + PatchImplementation lastImpl = null; + + boolean first = true; for (FarmingPatch patch : patches) { FarmingPatchPanel p = new FarmingPatchPanel(patch); + + /* Show labels to subdivide tabs into sections */ + if ((tab == Tab.SPECIAL + && patch.getImplementation().ordinal() > 2 + && patch.getImplementation() != lastImpl) + || (tab == Tab.TREE + && patch.getImplementation().ordinal() > 9 + && patch.getImplementation() != lastImpl)) + { + JLabel groupLabel = new JLabel(patch.getImplementation().getName()); + groupLabel.setBorder(new EmptyBorder(15, 0, 0, 0)); + groupLabel.setFont(FontManager.getRunescapeSmallFont()); + + container.add(groupLabel, c); + c.gridy++; + lastImpl = patch.getImplementation(); + } + patchPanels.add(p); - panel.add(p, c); + container.add(p, c); c.gridy++; + + /* This is a weird hack to remove the top border on the first tracker of every tab */ + if (first) + { + p.setBorder(null); + first = false; + } } JPanel wrapped = new JPanel(new BorderLayout()); - wrapped.add(panel, BorderLayout.NORTH); + wrapped.add(container, BorderLayout.NORTH); + wrapped.setBackground(ColorScheme.DARK_GRAY_COLOR); + JScrollPane scroller = new JScrollPane(wrapped); scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); - scroller.getVerticalScrollBar().setUnitIncrement(16); + scroller.getVerticalScrollBar().setPreferredSize(new Dimension(16, 0)); + scroller.getVerticalScrollBar().setBorder(new EmptyBorder(0, 9, 0, 0)); + scroller.setBackground(ColorScheme.DARK_GRAY_COLOR); + + MaterialTab materialTab = new MaterialTab("", tabGroup, scroller); + materialTab.setName(tab.getName()); AsyncBufferedImage icon = itemManager.getImage(tab.getItemID()); - tabs.addTab(null, null, scroller, tab.getName()); - int idx = tabs.getTabCount() - 1; Runnable resize = () -> { - tabs.setIconAt(idx, new ImageIcon(icon.getScaledInstance(24, 21, Image.SCALE_SMOOTH))); + BufferedImage subIcon = icon.getSubimage(0, 0, 32, 32); + materialTab.setIcon(new ImageIcon(subIcon.getScaledInstance(24, 24, Image.SCALE_SMOOTH))); + materialTab.setHorizontalAlignment(SwingConstants.CENTER); + materialTab.setVerticalAlignment(SwingConstants.CENTER); + materialTab.setOpaque(true); + materialTab.setBackground(ColorScheme.DARKER_GRAY_COLOR); + materialTab.setPreferredSize(new Dimension(30, 27)); }; icon.onChanged(resize); resize.run(); + materialTab.setOnSelectEvent(() -> config.setPatch(tab)); + + tabGroup.addTab(materialTab); if (config.patch() == tab) { - tabs.setSelectedComponent(scroller); + tabGroup.select(materialTab); } - tabs.addChangeListener(e -> - { - if (tabs.getSelectedComponent() == scroller) - { - config.setPatch(tab); - } - }); }); - add(tabs, BorderLayout.CENTER); } void update() @@ -178,10 +231,11 @@ class FarmingTrackerPanel extends PluginPanel PatchState state = unixTime <= 0 ? null : patch.getImplementation().forVarbitValue(value); if (state == null) { - panel.getIcon().setIcon(null); + itemManager.getImage(Produce.WEEDS.getItemID()).addTo(panel.getIcon()); panel.getIcon().setToolTipText("Unknown state"); - panel.getProgress().setMaximum(0); + panel.getProgress().setMaximumValue(0); panel.getProgress().setValue(0); + panel.getProgress().setVisible(false); panel.getEstimate().setText("Unknown"); panel.getProgress().setBackground(null); } @@ -298,9 +352,20 @@ class FarmingTrackerPanel extends PluginPanel } } - panel.getProgress().setBackground(state.getCropState().getColor().darker()); - panel.getProgress().setMaximum(stages - 1); - panel.getProgress().setValue(stage); + /* Hide any fully grown weeds' progress bar. */ + if (state.getProduce() != Produce.WEEDS + || (state.getProduce() == Produce.WEEDS && !autoweed && stage < stages - 1)) + { + panel.getProgress().setVisible(true); + panel.getProgress().setForeground(state.getCropState().getColor().darker()); + panel.getProgress().setMaximumValue(stages - 1); + panel.getProgress().setValue(stage); + panel.getProgress().update(); + } + else + { + panel.getProgress().setVisible(false); + } } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingWorld.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingWorld.java index a79c3f746b..f4fdba1757 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingWorld.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingWorld.java @@ -66,11 +66,11 @@ public class FarmingWorld new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("Herb", Varbits.FARMING_4774, PatchImplementation.HERB) )); add(new FarmingRegion("Brimhaven", 11058, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE), + new FarmingPatch("Fruit Tree", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE), new FarmingPatch("Spirit Tree", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE) )); @@ -78,10 +78,10 @@ public class FarmingWorld new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("Herb", Varbits.FARMING_4774, PatchImplementation.HERB) )); add(new FarmingRegion("Catherby", 11317, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) + new FarmingPatch("Fruit Tree", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) )); add(new FarmingRegion("Champion's Guild", 12596, @@ -89,7 +89,7 @@ public class FarmingWorld )); add(new FarmingRegion("Draynor Manor", 12340, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BELLADONNA) + new FarmingPatch("Belladonna", Varbits.FARMING_4771, PatchImplementation.BELLADONNA) )); add(new FarmingRegion("Entrana", 11060, @@ -102,13 +102,13 @@ public class FarmingWorld )); add(new FarmingRegion("Falador", 11828, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) + new FarmingPatch("Tree", Varbits.FARMING_4771, PatchImplementation.TREE) )); add(new FarmingRegion("Falador", 12083, new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("Herb", Varbits.FARMING_4774, PatchImplementation.HERB) ) { @Override @@ -129,20 +129,20 @@ public class FarmingWorld )); add(new FarmingRegion("Gnome Stronghold", 9781, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE), - new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.FRUIT_TREE) + new FarmingPatch("Tree", Varbits.FARMING_4771, PatchImplementation.TREE), + new FarmingPatch("Fruit Tree", Varbits.FARMING_4772, PatchImplementation.FRUIT_TREE) )); add(new FarmingRegion("Harmony", 15148, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), - new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.HERB) + new FarmingPatch("Allotment", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), + new FarmingPatch("Herb", Varbits.FARMING_4772, PatchImplementation.HERB) )); add(new FarmingRegion("Kourend", 7222, new FarmingPatch("North East", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South West", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("Herb", Varbits.FARMING_4774, PatchImplementation.HERB) )); add(new FarmingRegion("Kourend", 6711, new FarmingPatch("Spirit Tree", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE) @@ -163,14 +163,14 @@ public class FarmingWorld )); add(new FarmingRegion("Lletya", 9265, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) + new FarmingPatch("Fruit Tree", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) )); add(new FarmingRegion("Lumbridge", 12851, new FarmingPatch("Hops", Varbits.FARMING_4771, PatchImplementation.HOPS) )); add(new FarmingRegion("Lumbridge", 12594, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) + new FarmingPatch("Tree", Varbits.FARMING_4771, PatchImplementation.TREE) )); add(new FarmingRegion("Morytania", 13622, @@ -180,7 +180,7 @@ public class FarmingWorld new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("Herb", Varbits.FARMING_4774, PatchImplementation.HERB) )); @@ -193,31 +193,31 @@ public class FarmingWorld ), 11826); add(new FarmingRegion("Seers' Village", 10551, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) + new FarmingPatch("Hops", Varbits.FARMING_4771, PatchImplementation.HOPS) )); add(new FarmingRegion("Tai Bwo Wannai", 11056, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CALQUAT) + new FarmingPatch("Calquat", Varbits.FARMING_4771, PatchImplementation.CALQUAT) )); add(new FarmingRegion("Taverly", 11573, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) + new FarmingPatch("Tree", Varbits.FARMING_4771, PatchImplementation.TREE) )); add(new FarmingRegion("Tree Gnome Village", 9777, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) + new FarmingPatch("Fruit Tree", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) )); add(new FarmingRegion("Troll Stronghold", 11321, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB) + new FarmingPatch("Herb", Varbits.FARMING_4771, PatchImplementation.HERB) )); add(new FarmingRegion("Varrock", 12854, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) + new FarmingPatch("Tree", Varbits.FARMING_4771, PatchImplementation.TREE) ), 12853); add(new FarmingRegion("Yanille", 10288, - new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) + new FarmingPatch("Hops", Varbits.FARMING_4771, PatchImplementation.HOPS) )); // Finalize diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/PatchImplementation.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/PatchImplementation.java index 13ebe7c43c..bbd3a655e8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/PatchImplementation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/PatchImplementation.java @@ -970,7 +970,7 @@ public enum PatchImplementation return null; } }, - BUSH(Tab.SPECIAL) + BUSH(Tab.BUSH) { @Override PatchState forVarbitValue(int value) @@ -2271,7 +2271,7 @@ public enum PatchImplementation return null; } }, - GRAPES(Tab.SPECIAL) + GRAPES(Tab.SPECIAL) { @Override PatchState forVarbitValue(int value) @@ -2299,5 +2299,16 @@ public enum PatchImplementation abstract PatchState forVarbitValue(int value); + /** + * Returns a formatted name based on the enum's name. + * Ex: + * SPIRIT_TREE into Spirit Tree + * HERB into Herb + */ + public String getName() + { + return (name().substring(0, 1).toUpperCase() + name().toLowerCase().substring(1)).replaceAll("_", " "); + } + private final Tab tab; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/Tab.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/Tab.java index 4f1919d10b..b8838742dc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/Tab.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/Tab.java @@ -36,6 +36,7 @@ public enum Tab HERB("Herbs", ItemID.GRIMY_RANARR_WEED), TREE("Trees", ItemID.MAHOGANY_LOGS), FRUIT_TREE("Fruit Trees", ItemID.PINEAPPLE), + BUSH("Bushes", ItemID.WHITE_BERRIES), SPECIAL("Special", ItemID.MUSHROOM); private final String name; 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 index 7714042049..128c88197a 100644 --- 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 @@ -24,6 +24,7 @@ */ package net.runelite.client.ui.components; +import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; @@ -32,6 +33,7 @@ import javax.swing.JComponent; import javax.swing.JScrollBar; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicScrollBarUI; +import lombok.Setter; import net.runelite.client.ui.ColorScheme; /** @@ -40,6 +42,15 @@ import net.runelite.client.ui.ColorScheme; */ public class CustomScrollBarUI extends BasicScrollBarUI { + + /* The background color of the bar's thumb */ + @Setter + private Color thumbColor = ColorScheme.MEDIUM_GRAY_COLOR; + + /* The background color of the bar's track */ + @Setter + private Color trackColor = ColorScheme.SCROLL_TRACK_COLOR; + /** * Overrides the painting of the bar's track (the darker part underneath that extends * the full page length). @@ -47,7 +58,7 @@ public class CustomScrollBarUI extends BasicScrollBarUI @Override protected void paintTrack(Graphics graphics, JComponent jComponent, Rectangle rectangle) { - graphics.setColor(ColorScheme.SCROLL_TRACK_COLOR); + graphics.setColor(trackColor); graphics.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); } @@ -58,7 +69,7 @@ public class CustomScrollBarUI extends BasicScrollBarUI @Override protected void paintThumb(Graphics graphics, JComponent jComponent, Rectangle rectangle) { - graphics.setColor(ColorScheme.MEDIUM_GRAY_COLOR); + graphics.setColor(thumbColor); graphics.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); } 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 index 692b9891bc..3d84cb483d 100644 --- 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 @@ -33,6 +33,7 @@ import javax.swing.JLabel; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import lombok.Getter; +import lombok.Setter; import net.runelite.client.ui.ColorScheme; /** @@ -59,6 +60,10 @@ public class MaterialTab extends JLabel @Getter private final JComponent content; + /* To be execuded when the tab is selected */ + @Setter + private Runnable onSelectEvent; + @Getter private boolean selected; @@ -93,6 +98,10 @@ public class MaterialTab extends JLabel setBorder(SELECTED_BORDER); setForeground(Color.WHITE); selected = true; + if (onSelectEvent != null) + { + onSelectEvent.run(); + } } public void unselect() 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 index 5e50953523..67978efd37 100644 --- 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 @@ -59,10 +59,22 @@ public class MaterialTabGroup extends JPanel { this.display = display; this.display.setLayout(new BorderLayout()); - setLayout(new FlowLayout()); + setLayout(new FlowLayout(FlowLayout.CENTER, 8, 0)); setOpaque(false); } + /* Returns the tab on a certain index. */ + public MaterialTab getTab(int index) + { + + if (tabs == null || tabs.isEmpty()) + { + return null; + } + + return tabs.get(index); + } + public void addTab(MaterialTab tab) { tabs.add(tab); @@ -89,6 +101,7 @@ public class MaterialTabGroup extends JPanel { tab.select(); display.add(tab.getContent()); + display.revalidate(); display.repaint(); } else From 3526b258bb09124f9158bf1d4892c5de2569873d Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Wed, 2 May 2018 03:23:54 +0100 Subject: [PATCH 12/15] Plugin side toolbar design tweak - Added new parameter to navigation buttons: priority. - Ordered the navigation buttons by their priority and name in PluginToolbar. - Added a maximum size to the navigation buttons to keep all their sizes the same. - Added new icons for the following plugins: - Account (login/logout) - Grand Exchange - Feed - Info - Kourend Library - Notes - Screenshot --- .../client/plugins/config/ConfigPlugin.java | 1 + .../plugins/devtools/DevToolsPlugin.java | 1 + .../farmingtracker/FarmingTrackerPlugin.java | 1 + .../client/plugins/feed/FeedPlugin.java | 1 + .../grandexchange/GrandExchangePlugin.java | 1 + .../client/plugins/hiscore/HiscorePlugin.java | 3 ++- .../client/plugins/info/InfoPlugin.java | 1 + .../kourendlibrary/KourendLibraryPlugin.java | 1 + .../client/plugins/notes/NotesPlugin.java | 1 + .../plugins/xptracker/XpTrackerPlugin.java | 1 + .../runelite/client/ui/NavigationButton.java | 6 ++++++ .../net/runelite/client/ui/PluginToolbar.java | 8 ++++++-- .../net/runelite/client/util/SwingUtil.java | 1 + .../client/plugins/account/login_icon.png | Bin 566 -> 15908 bytes .../client/plugins/account/logout_icon.png | Bin 365 -> 15907 bytes .../net/runelite/client/plugins/feed/icon.png | Bin 15726 -> 16187 bytes .../client/plugins/grandexchange/ge_icon.png | Bin 15952 -> 16103 bytes .../client/plugins/info/info_icon.png | Bin 1810 -> 15521 bytes .../plugins/kourendlibrary/panel_icon.png | Bin 2320 -> 16403 bytes .../client/plugins/notes/notes_icon.png | Bin 503 -> 16255 bytes .../client/plugins/screenshot/screenshot.png | Bin 692 -> 218 bytes 21 files changed, 24 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java index 08a379875b..7a6cf9e7d8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java @@ -78,6 +78,7 @@ public class ConfigPlugin extends Plugin navButton = NavigationButton.builder() .name("Configuration") .icon(icon) + .priority(0) .panel(configPanel) .build(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java index cebeccac04..9c0ebab009 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java @@ -122,6 +122,7 @@ public class DevToolsPlugin extends Plugin navButton = NavigationButton.builder() .name("Developer Tools") .icon(icon) + .priority(1) .panel(panel) .build(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPlugin.java index 5177308528..5af1974cf5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPlugin.java @@ -98,6 +98,7 @@ public class FarmingTrackerPlugin extends Plugin .name("Farming Tracker") .icon(icon) .panel(panel) + .priority(4) .build(); pluginToolbar.addNavigation(navButton); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java index fa8bff265a..d1970191d1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java @@ -93,6 +93,7 @@ public class FeedPlugin extends Plugin navButton = NavigationButton.builder() .name("News Feed") .icon(icon) + .priority(8) .panel(feedPanel) .build(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java index 21078d646f..1a2788e0c7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java @@ -114,6 +114,7 @@ public class GrandExchangePlugin extends Plugin button = NavigationButton.builder() .name("GE Offers") .icon(icon) + .priority(3) .panel(panel) .build(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java index 499a8777e6..5ad19a9f55 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java @@ -98,12 +98,13 @@ public class HiscorePlugin extends Plugin BufferedImage icon; synchronized (ImageIO.class) { - icon = ImageIO.read(getClass().getResourceAsStream("hiscore.gif")); + icon = ImageIO.read(getClass().getResourceAsStream("normal.png")); } navButton = NavigationButton.builder() .name("Hiscore") .icon(icon) + .priority(5) .panel(hiscorePanel) .build(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java index bb14b4ea3e..0b1905ff98 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java @@ -58,6 +58,7 @@ public class InfoPlugin extends Plugin navButton = NavigationButton.builder() .name("Info") .icon(icon) + .priority(9) .panel(panel) .build(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java index 154abbcb9e..83406e0f2d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java @@ -94,6 +94,7 @@ public class KourendLibraryPlugin extends Plugin navButton = NavigationButton.builder() .name("Kourend Library") + .priority(6) .icon(icon) .panel(panel) .build(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java index 0cd89cfa66..a6fac2de52 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java @@ -74,6 +74,7 @@ public class NotesPlugin extends Plugin navButton = NavigationButton.builder() .name("Notes") .icon(icon) + .priority(7) .panel(panel) .build(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java index 1a97034673..e99e3d16bd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java @@ -122,6 +122,7 @@ public class XpTrackerPlugin extends Plugin navButton = NavigationButton.builder() .name("XP Tracker") .icon(icon) + .priority(2) .panel(xpPanel) .build(); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java b/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java index 9da89f2185..9695c765fe 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java @@ -74,6 +74,12 @@ public class NavigationButton */ private PluginPanel panel; + + /** + * The order in which the button should be displayed in the side bar. (from lower to higher) + */ + private int priority; + /** * Map of key-value pairs for setting the popup menu */ diff --git a/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java b/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java index 4ef441f7a6..5979a133d7 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java @@ -24,8 +24,8 @@ */ package net.runelite.client.ui; +import com.google.common.collect.ComparisonChain; import com.google.common.eventbus.EventBus; -import java.util.Comparator; import java.util.TreeSet; import javax.inject.Inject; import javax.inject.Singleton; @@ -39,7 +39,11 @@ import net.runelite.client.events.PluginToolbarButtonRemoved; public class PluginToolbar { private final EventBus eventBus; - private final TreeSet buttons = new TreeSet<>(Comparator.comparing(NavigationButton::getName)); + private final TreeSet buttons = new TreeSet<>((a, b) -> + ComparisonChain.start() + .compare(a.getPriority(), b.getPriority()) + .compare(a.getName(), b.getName()) + .result()); @Inject private PluginToolbar(final EventBus eventBus) 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 90790cbf36..82e7ec726d 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 @@ -310,6 +310,7 @@ public class SwingUtil : navigationButton.getIcon(); final JButton button = new JButton(); + button.setMaximumSize(new Dimension(30, 30)); button.setName(navigationButton.getName()); button.setToolTipText(navigationButton.getTooltip()); button.setIcon(new ImageIcon(scaledImage)); diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/account/login_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/account/login_icon.png index eed416cfd179ecefc200881a3a52a98940fa7975..1ba7ac1fae9a6928cfb6f1764abc7982d40f204e 100644 GIT binary patch literal 15908 zcmeI3ZEPIH8OPTN0b_6!5fEuUl+C3eC(ho^-rnBJa=zH{CD>6eIc%5MsfgIyowGN- z+uiK0b7y0a6~hPAHh_Qx(Sk$*gttVk`l_ZafkY8er3L|MC`t=T1>r@|N)>7m0W*8= zH?~Jno2o*ZlkWDOdFKDj&TpQ1cK5^Hm)d+wYs>N$hGANJdlG%5P~b+uPmETDFdB4zHQTqJzWbg7-@D@a{7Xkq z-fE>TESTxs`O&*eR!mPfKk?z&WAE87Kl|3$LFHj($>?cAe*Lq_=oiPY?q^=R@@H=v zzgWzi`_7(!zkI%Z!Mo#c9KN~rs}qxtu5R5l^@;KDz3l4FnRN%3A72Kh?|#1PY|}Hc zex@_VJiYx>zH`;9U$(ZbI`hx2NhWjZvq|tiBffgqA~5DY*R-Qo?i>DHKlj33*n8wh zmpvH!Hk6lZ_S(fuAGzuI0|)#M%v^Ca{rk5J**>&m=+2KXh=qndARyd_qUQGgtD6}FJe8jf7(@D=0A=)c#@``BU^ceKOThd334!fZI|7o>1F7+%8$fFSWe}UNESH;8>A356p))DZY|a6l zfpH7+eb6z-Y!$BG1r2xQTqmKa)i~Ceoh|awR32tw4oMtz^1_^_U{y9+Ro-wKRrkEJ zclAtdaT>IqXI>dB=<~=tPf1oxh0+SeQ2xo?HdI{G9x%;pyp(0NGJ&dumsM=rR^8Cd zyt69C`n10MrF3^~7Q%QSNJst29QD1^|!+ zNfx8^Q5yQt`k2e<$4JL`I63!@qgFz|71v5d7i$EcY zW)*<}F%k&Dpjw&B9ARb+)uTHoo5t(14CPv}^7O-5i&X{&iapQVU8HT)N49ECdV=Y@^wW~+BnKkXCslm7yR*de*R3v$cXuMA%LFP}O0hf(F@9DX-ha?5_t~*x|8M}H4=;GX zWof+|mkyDo^=@2RQ-0Ub-FTop8rEE=@@}EzBGZ0!w-4hXNfM{?wC_JG@r1}Q5Kdms_wqLSmaBs=&m04;dYQbGe`+(jlu@x* zbPx;(*`&D0d@wJ?g#jU(6c?Ef=B2nWAY_x`BJ;t#6c+}BY*JigKA4x{!hn!Xii^w# z^HN+G5VA>ek@;X=iVFimHYqMLAIwW}VL-?x#YN_Wc_}Uo2-&2#$b2v_#f1SOn-mwB z59Xz~Fd$@;;v)0Gyc8D(gltk=WImXe;=+KCO^S=m2lG-~7!a~aagq68UWy9?LN+Nb zG9S!KabZBnCdEbOgLx?~3<%kzxX64kFU5rcA)6EznGfcrxG*4Olj0)t!Mqd~283)9 zakbRm;e`fzaW{|N(0z8V?*sI9FRS+SB^hSoMuwT1W|+@s&=IxwH8V}u4Kj?pk72Gi z&uqKl0o2#*O>_+u-cDbbnOVC~Vy+4tbcX{cpV@Wf6g&=^hjuTU$_h&tKEmX#e&u~G zee8$#z4;S%+2}pj?)!ApSNHS2o|cu9dxZxd%KT%Zb)WCi_U5Pgn>OCQ`!t(XI2uHT$EyzY+OUjrf4aO400 literal 566 zcmV-60?GY}P)VGd000McNliru;RhBG4F;FDr0W0x0mDf| zK~y-)jg!qwRACgxe`oG(#>r+t7pde}k>t=KA_&@82#O1#Hf9h6E!y<4tAD`1z{sGT z1T9(wQo9zS;<`_YFa;MbgvKElM(^A^-g{4r>Am+hU1=V8;c(9TK0N0+=M`1O;A>?~ zKkY6f01BoeBE(QYxLUf&P_ZnwNnc-(!uW-Iyj_2OCTQZ&9Y4u=i1R15d;-Z*s(ID- z4=>IW5#$QwEjhx{E#2Zjn0YOABQIl;`}?3MoxrPOo4XN#=Occ20Kl8gXS(t0!{KaI zFt$W3EZxL|HK?pS9>_GZ88=xoGhe+fn z&A>%f@dAev$A+0WeMfGLJW6A45lojK|Hb}pYeBc`-#LGLjLQR4=>nx~52jv7|I?2T z6rdTpj1Ep8{x}vu(wNW^3xI3G_k|7-DX_ax>1+|n74Svd6*#zo`b@l(p`!;h{7me+ zlL$fxv%Qo|Rg&NNb7fh-R949(CX(8`z?ISrCyVFgHv<`?KWzcIYybcN07*qoM6N<$ Ef+QUKK>z>% diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/account/logout_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/account/logout_icon.png index 8d8ee8c5650061114e9d113c31f5806c08c0141b..b1bfcd111544ff2768f1640a61818af01ac4e8fe 100644 GIT binary patch literal 15907 zcmeI3ZHya78OP^JnkH8+I$EGfK7ixU=yJ(=U+neE*5|v6o%USCiR(L>OF&WAyW_j< z*}I$Fz1VjgRYcm-rd3Fxg;HLmCZ(k*i9~so3T@IvQ9}hqKu8*;qzWomS_&Uf1&EYm z*6*J4jUos_mBy02e&(6~GdsU|=Gom3dw+WKEnS@}I{^S)$)Q9B9j|l0OBSQw+kdfd zD>^JShql=OTzkFy?EsH`eK`OfK`kCnr^gM)u*VJ4lZ?kbre)-`JOrS0?(n|v-g-R! z;el5kIB+q&ZqZJ&^rfDjAHH?R$tReN-QPLA>eO$ac>GrFx*a=C-*xxFPhE3E@r9$O zZZp%Du9_Rz`PS>*tM=?!^36YjKLGzrpS_x!be`_GGbv@pemcxN^JSQP z?rV!4ihc}AD^;t1+4YaCIdt%#Z~xpiN3+kL)g|ljo!dV1_9g$3*_Y4%?hDp&F@j$Ta8f|1%W|57<)>011*!DJVx&;+tFj}vMB(`n zJ|uB|iED|{(ie|os9J97vZ6G4w#G=Mq@->;vaY~nB8KMf(==5Q`7keXDr7iCkQpck z6ei0BB8Z|dXDMRn)mSL9Gm<`@_db7-7^%7h^w zVS-8+3jVMVgn{~8+Ju=m)R1PQY?^AyGL&oO$}++ zu~y3G*U+e{NIAnQ$WBZv$YYQ-^|2`1Xl!Ilv~l%_8+pS@87hqVqinn8wkq^!kghx# zREtjLmJOaBbXOQgSN}$orfyAP+`AJAT_#Y;lFQ|pkFxXH@cyH2xzA6Q`-cP$eOTW8 ztxB8SxO5v?+U&-q4do|w&4~%s(Xi$wm3s>%m6;BsyL}i7ilSePvUoGDNNXjSA5CcJ zLT|elEFN94%NpF#R=8~;VUwdOtYlQveR;XaS6b0sKYHL`>rWi>>VN$*c_sfeW^^T^ zVzKBT7!a~aagq68UWy9?LN+NbG9S!KabZBnCdEbOgLx?~3<%kzxX64kFU5rcA)6Ez znGfcrxG*4Olj0)t!Mqd~283)JkWGq<%m?#QTo@3tNpX?+U|xy~141?_ zE;1j?OL1X9$R@=_=7V`DE({3Sq`1g@FfYZ00U?_d7nu*{rMNI4WRv0|^TE6n7Y2lE zQe0#{n3v+hfRIg!i_8b}Qd}4ivPp4~`Cwj(3j;znDK0V}%u8`$K*%P=MdpKfDJ~2M z*(BoXY`nt@b@bwH5xt?i_lVYdNMIMQaYEtG&k4(0TEm)%szCe_tcX=d+qt3-zRrJyNCy`X4fq4?Krpm z?msb^;}6`w|H7%gq7ye)+{bKqv}bbZG48PI>7@|yLT7gs*^MfWA-cXCYt literal 365 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=DjSK$uZf!>a)($X?><>&pI+T})6&FF%5*A1L(8)5S5w;&k$#|Nrd|Kc6w>KaUE- zJ=J1~#2hh+$KNldZaR{Y*4tP0r*ERS+l&9()iu~s*8KWc7W(4<`xc%bGLxGQr8gPY z)wxNyaV03ZU0lCa`v3nKn;+Omvi*tL{qg_%%jtnk0?+EF7$_|H`F?ISPrUZ4fS7}K zFCOW*+VCZ^XN~a8XaAL~dGc)26|7Q5H zW0AV!2V8y94w*DuJ=oUh>GsOd(9CGHhT#Xjnauv33>GiK-v-M%90!I1gQu&X%Q~lo FCIFcLn63Z- diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/feed/icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/feed/icon.png index 95b048b153ed765e96e4a89a07eaef0313b01f46..20ef822883b47558d8b2adf149d58d11edbe4e94 100644 GIT binary patch delta 1788 zcma)5dsLDK6#qn-Mq-wt>B?Z4j~w!S@bT0w5KS!;qUI}O0zzU-%xb<+Y-Eo)EBe&3 zOjAqE$`n`W)RJjr)8@+5%#^}yK3cWq)U|1j&d$!x>1^lhk9+SqzjGhwckk~$Pix8~ z9O8!tVlmDb006MTLH+_$w0~n(@0fhrVpp6Z5g*Lu;uDh; zGWjP`07!Xw>2Te;HoiT(JEzRZ_p;cTn1XnCT%Cx~pCty^UhE~@?mJt$PQFAa?9JR) zzQxip`9|yQjfwnO)I59V#A926T5VN4{;2JTBwf{mJ(Z$7k?r>01lGMNCG(GV>rKFS zss;7;6Ry|*M%Tl?=|;U!k5hYUe6X+e$`h-xAz9B7^0M)(+4Fvt&h1MYPl$z1bN5e;Kvm(;okK!;kj~Z5g zaoR9y)TcPvC`4(3FNeGu2C}lBmR$-fZAQ;pLXea}6hR=9C;?$Ql|cs~ z2?az}qSv^AGAflCOOp~Mu#9XP0YgNQL?$A_5~)-s7Rz8UMDZmd9JCWcLoY_AF-wqk zbgAqADMM_n7;mv#gxP4?kO%`Je9NN$$ML_LR3Tm#F+TrrHp!g-m&!lnyjcof5a@^Z zfS62%r!VQ>((30hBr_BlM7*u(7G%&wlW&cy&$NypsSf4N^_xBlF9{NC0D!br0Fb2y zfLHUTI0gU$E5HmFVG_x40B}_FN3A#t0Ossqf6nHVhZpm@Ks+&Y^XjEw@mS zZ{pM)_EAx1tg~zEA`6}JQ$N)OO>-8mS0YGJ4)SsFOh(CMke`){Ec@r$^;Wuz>pVLM zIxVeb+pb_;`#1XtsJi((@ufM3$56jcnmP8od|Bq6 zRBdOJ#Q6^H9~yTXXb>;2b!?wMUCLIKMrbRw&HnNgOUqO-Ctmnm)`-&f9lzjpZG6wh zO3vBt&u*3#l#z&@%DZ_5Av05%U!^Z=*H7>-a5dCY&E@7VcdQ@Zl-d%NJ6kF^-xUdX zd>4Pk=YsHo^9M9q=wxpFEZr$H_(?QC(J1 z2ao9;Z(Y|>l{vYog(0D7cdKyUOSfqW+pz-F5FT9O226uX=s;f9-MQ+4kQy=PEV2i`pZYc1^>xmvr?!+SJlTi&% zCs%f^&TM{yr@3M32>Zl84gGK!ZZtKr-uiV;ROdl%{)enrRk{fnnk8Bi% z9SkdI?3=OEuI=2S=nuJ1kIVu34f%7H4Uj>QUxN4@zl3a&B-i=`z8WHpH9rhg7;Fj0 z_VrNxwsN0BXSRL#U`^JJ9n0O{<+%iSzDl&V-uZ{)3Mp@NvVO+<`uX&W!IY$%eVucK zg)=A}P|&rth&zJo4r@`VNHcZ!Uuy4dIC-!t&Ubi$TgHvWPA+SIBNq;i2XL`O3xUX5rCO^2^NH z@Q7}uTVG*#dRA>rVHUDL#08n&}!DnbqlO%`UeS0hHDjs9dqHp$6qY=V;) zv29i%M$05l3%GY6Uf;(a2n-Sdjx#DI#z_{5DXFQt7Usq&x<;u$&sbO_8R?oN8m5|? zTACzTTBafEH!-j>wVVvoZ!md32iN3_oc5E?vbj#4&EZH?;GmcX4s>+$@-{!?Qf6c_ zG@H!LW6oq~ve|_vU%&o~URMM#H7F!|1o<*BRH-sBG&D0X{QM83UotS18Za=tN?>5H zn!&&zoBZgoU?IoUxIMoa;%b`b#R}B2 z_J3;>=?<}3nX4QYoz2xM6zwGMy5aFsPS44!xDPvR`ux^s6E_1x{SvMdh6>^h18Ke?X}eFvdPW&b_J4Wg?FxVzy38TV&Tkr+b0`}zWjQ0dE$)9j>l8}n;e(g z%lpZ5%BIPz4I*dbPiO4w5>x#9FE~*2VTH+!UD;bW_AfsAd{u3+=xphR2+a-#0ly}N z^mW(2b8`4zUew{}kP+E+Hf?f!)N3Aw!ko4K5n7fkEzi@J^Bg%$)FAGhNET7B20L83t>a@wO`|Nig?NiS$N c<*{dG=uB~EzH9QK9T*i1p00i_>zopr0NwsTI{*Lx diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/ge_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/ge_icon.png index 0783e0cd32feed230a3cc4288e9f6d9dbd791093..6455733f8fadde9fa0d204d26aae67c2b32d83e9 100644 GIT binary patch delta 1584 zcma)-Yc!Mz7{}ikGt#WfY{|&ZVWu7B(wO&U#$2#QOhp&hwNhbjFOxE86rqvSrlQfC zILBH=E)k_crJIn_jTlCanvT*)$y$k4nRU*d{Zt?N{Qo`YJkNQ4&w0Ajbm!Vf&6du<_<+x7JvU+W1)jWr zztNv>2HS=cZ&eK>Rq{{p@v(hzoSvyP_H4UxFwiYus8q!@ngTXHKJa=m`$4ZpwX}UOnd39NtlS03sMQ~$a zbsQ(LcH5@Zx1U|=boU3JAH!#a?O7S_#R}1B9gY@jbweG=OHHNxJwXl694|k~IPkW# zHuUs0?DR`oONalLk#K(j0B~IDIdLe2j6Q%=p;C|v^gTR-3PK?en*+LX=qyJNLNW{} z$cVwBuLB&0jFcEG`8sGfScc?cEGTrb*qQCj6jB*1p^!>v(1lb!QwUR;d=LU%`3$j$ z&PMKH)+}Q(#4yaH(nYR(Dg=pHR6YcWU!bZBy<502 z#3AEGafl-($}RqXM>+G32K=Y!FJ2<4Kuii+vx;N~vDqwpciOiwW#GaX6dGbEui~UM z45pt(MVvSU`3L|683CvzVe>02004qi9d4tTKaF?3K3*ZoPer|M+czhf-Q>ph1&oW8U|9f`LBcJJ#lC7lfU9Ur`)4buw+G-Z>+c#;~k@Htl2hv^^W zI1D@L4f?cX0FCzdemnkhv+d9WAA;U^_J}?|4esPs8?UizYAu|jl&{=y&x9wwksGVI z47bEBGsR%>uMal>Y*nPdX{^3#S^xCvbK@9ycV$siCRFN4zIewtGmBriPc?2}_0ION z_&41ls=`lf?huW??d};l{9*gl;F$bWuS{kF=>=! z&ZTNT-_EH(@VT0^% zQ-~!J_vYlJPY;T0FB#dt@%T)tAmp=Piv|sI(`?HAsl6=|WxX95f0g50RF=H5rD0{{ z>|d?1wDS)zktGr7&p_Cpt21odSX86#pAqkF&F&{UZYb!UG|FG647?wEwDqx1+r_QT zMMVX#uGQ3eOn*R{@2>j$s(NA<=Yz&0G<;3+u*x{6+*W4WJ+zQ+joO|*qdAs;ttxMc zQ>n&-W)t~GPu}rtHThwb_L!Z#V8^!L;}amoWN&4vv>@`5cgH55lV^;Dw@QkN8xuKm z?^T`svpv;aTZl8AcMj^1Sc@m8yrWmVdh{`)HlUtiymlwg^Qbn*Ims=^%1^yX(LX(@ ztXq-Ag(Y*op~Vjc`sc&(!eW|ywCCd@9DVtffB45O#(lSfW8s&oxrN>~2M-2P%*}q8 b7)Jp(r*Ig~KU+8d83142MxVM>QJMb&w&t8p delta 1465 zcma)6X*84z0R7NR$WHc>v1Rg^eU;}lW+);_$}*TSGw3mx7Ll41nR%l$^F^cMNWD^& zQ4Kkb)1qUKP?DvP>{|;xl%A*G{dvFck9+Pt=iYnH9Y5KTe$h;Oqb3{xfZ`R{jMUal zoid|LSL3Tei5jWUlzJ94q0wT10kPyDESieNQ3)gvO@yAWvxi(^1_oFVO9;YHsAx15 zNBl+j!w}HOI+NcCO_(j@p&bP!z%1-}92Sv7qL4r?hlB%JJUj;^W65}sfM@a01PqVO z;t-%Rm^Xq$An>r!AcjRjgBT2#46^V<63C`t*cb{4i{_GukTKlDk;vxZIP5h)43CTj zF$8=x$i|R)AQwmAtodR|Y+Uqq6oi3WK#p)j=sH|!|9>PhE%I;4m9^`w)c#k1U+UlL zsa-FGo@#1Comzb5n0C0DIu@g(>m;Itd%(@+Ya?ul-Wliz0BH^YaQZ9&EU&KNBmf*m z1He=i08r%sV3sJ0toH%{je%YC9YM$YGB1V2zdmZx*#2qt)6-d1-ot6Hw;xm+-apRL z4QI`E`(>8jl~|#Pq$4^F4U7g?oo3Slxh>fUC1qr98Ok!S$T)4;=^&&VpG1H8hu(Eu zj55Evb-RV#@=`He)Sph)ccLs89)3a@--htvZD8(ud`xnozs%uXa9)$3Ah|?-i*X~6 z-pA;AUh_>!qsw$=vv0SzT3+>H=3fWpySb;3h%CE3Aacw(iEAGB%vHHW25ZXw&hy)Bv5AOqL~K17?S+<^umP*TQXX zFYb=na$T=Yc|mlo^zoNr&cM51T4Il1W1v+9-+8Q;XJcD&&M0+k4mlso-usnVcPAE! zHqTL+OF($5oO8wC5sLTwRqO{ zqUJES7IL4=`-L`2ceG%pG@?$D%aO*t6)x8t4;EGCrRq3dF-=a5lo#A5q$gKOuZeO$ zD2(`#(as+Nj>$n^F@V6 z3J$l|Z(hjWh7b%Fxk-1HFk=pf811R}hVBh%jme(sJrLPcTRUq1ScwbeTIvw(G5CfuV>O6CeLiSxs!)Lpwt-&0ou3#TI! zhFiN1IXaas38*gv@+2kc8Z{v&!Cj8vePn;sz3#SxP2AR;H_372meK(ralvJDsT+O1 z*5x@Y&U$WJ%zLCwH9vklYg5+UZ`u~kp)^@r8oPYZ$GfBDpv&0<#LG!sp>BKikJK8J zu&(NCvce{;vC~`QNQ!Cld?T2nlYMZEFrgj#xhIH*zsU^$@TNC_bFZ41XE&{+)cn|S zAyGH!=#9T_S-$9b1Oh_6XKl=tg|=wXR)huB%D23ywU1#aeY5P(@GKJRW^mwtzPAn; zk3Qsky7VP2&2IAwMK9D!AkM^6$PZ+~0TJ}wdJ`mDoO6HZBq)TBZpQh+cxgykZ#D9K z#v!K@h-1gH_e~qqMb*VH75iNrw)XILuSA{6_yNCX5x-0z={YHJ< q-a%FQl)q}flp~u*Ajy|)RtZ3=XVH9_NtIyj9e`a7f4YJeb>=_3JZj$n diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/info/info_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/info/info_icon.png index 565f2a48b259a09ac4f977bcb06d33910dab5275..f4d052870a527fcc25368a8811ecf5d128aeead1 100644 GIT binary patch literal 15521 zcmeI3du$xV9mmIU9D<31fkrr~+Fp)QoH%>Cd+%G$7dyUyFXEEJHejbxPjSrG)pswG~E&F>T>9YmQpehfc(1$@A}!Mml9v; zc;k+T&Lr9v@6hvmTU&qm;oz}{nT^d)9bf&*iH9HDq+Gmh+wq&fyZ_tgU7CAw@|A1# z#MwpD9Xmce-Mo5yeCY#!J@wLGj3bY|J^ZM6kJ!BZxGMba(_Nv@k1p#4Z=V13@6}%{ z2WP%{*Jnqj+7_K2dF#-XOG^iYsUVj-ZSA?+cCZ2(N#xVxbd5x=sZ<-Ku}J0 zgux@*{?2x+dHGyZ>zb3FbnXJFH$L6Py$?Juf2V;PwjQs$F(&k+U+86?`~i$T_v6KP zhrbSmRkE>u`6c&$>xupQU3;d_n@s-ZZB;N1-Z*g6M`t~Urd~Vsr|&n9^^dOG+8OMd zdT;HWKY4HJ%(48+<(fF!^UNRHo_%L*;-iNSUjN|p4gdNxJ9%u&xevB(Ndh2vl)iqe zKYooMX<3&jYf0$JXLZCIfbjafE=t>><~pZFX}As;@Eja@inB^99I>j*b|EpN4YPHmbj28HGT0o znyjQo78Iq@vo=ONF2q#R5>*Mtq7gKAm!il5=l8jjAwIzP-9d@*hP(kL=<%f(=;wU0 z&P*t#518p%BRF}j|RUdmUG8#-yd4d5sATv<*x|tyKN=z{64Kb+z?+^Oj zP;`ssA?64(Yp8C;MA*%I6kAi+ z;DRXGwI?Fk=?mpBThm&(t+KKtQ)Vyc!VnbWSIOreBR2$9Qu-G|6?)Ad#MDw&PBdU= z8fC~rc4jeZtqczzX6;&?QT_)DVTwEdyJc9|uKK@NhDsJoL!z37a)g~p;F*Sntz2 z=gU&+_FTQe(TdI%LG%o%M5*dl6~=zX38>hh#1{*joDZ|J+VKAUcG1WT7y6^Ji7uA6 zzf00;H!d9@ORL?uw4%IDRji1+G#b`irLrHyLV;;7Iy1tE-{hQ| z9RveHHYqMLAIwW}VL-?x#YN_Wc_}Uo2-&2#$b2v_#f1SOn-mwB59Xz~Fd$@;;v)0G zyc8D(gltk=WImXe;=+KCO^S=m2lG-~7!a~aagq68UWy9?LN+NbG9S!KabZBnCdEbO zgLx?~3<%kzxX64kFU5rcA)6EznGfcrxG*4Olj0)t!Mqd~283)JkWGq< z%m?#QTo@3tNpX?+U|xy~141?_E;1j?OL1X9$R@=_=7V`DE({3Sq`1g@FfYZ00U?`2 zT#c1CL7|FX|IDHHJe5=L$Iv^X4yn5*4#4Q;0E~?T@b78#`vCwwOF`X=e)M|j4*|GT zJGu26dr;p4v1n&s{@tWJ+1N5I-F_~8G%*o>>x*-rbI%BZAh!H=_fr=Q^#0SSEcs-8 z@5;?Tmp$vgchjpMe|)5V$w))PW2>9%?>nr>gM)+Iv}eV{t#!MF-530*MIYO{?wwZe zi9JVBZ9@LD({=Ue8h`$lMVndpC-G9aFe*o#J%Nr)^m#yUZ1ZX{e_NN!! y4R+Tbe)hnvPv7>duZ1SAZOiU`;7L#q_Winj@v;}by25@1Hnwqd^w5UETmAzj>kJ+M literal 1810 zcmcIlU2NM_6n0y+T2rlLgVC-<)Oa9Z;Mh)L$M%*sZPG4bxRfqZyBD@=-1|DLx_gym=Y2sUI|B^q{F-rc}a~ZbmPa6Xb#M&)2WF@EOHfR z3Z7@H99J%vL*+;aJ9$n}6ouo%TsX`U1nVwY9;mREyQ9gFLayPMwr66C_8EbW7rZz_ zl-5%y*)3YjZIp==jH>{f6GFV7QWL0Y>qASW7TWd37fF%}Z27^Xxuf5|`-6HjSEU zve3yS{n(6YL0onp_w_X3CH;cBFKmpfg-aqjEbzB(*di*cpxcyRECCJ(?#~D zu(9A5&);8fT8It8(W;LMctMFlS!SUj32Y2SAuB6UorR(tlXVG2WGUBVd&V)zdjpEM zv-+waVa)L|5TiNBN&?EUl1NnPQBh`fL6pLL4u}ybGBh;Q9Ck{8bctC4dBoXPo}vFy zlf*^rXc&^3NBmwU>?Dh{=@R3X8gIuwG~ZYiO}f@{Dlq&`k28jEF@j9vwt0u@ZA@pK zwp>7jbRF~SFc;^%GH}pno>cljT8q1zbQdhHFMMOR|8K$@B{d4b$|Eu|ImS58Zu@85&U+rX07oy29-F+ac?@do6KIuJnXteTZ@cTld02V2VeRdh8sn- diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/panel_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/panel_icon.png index cb569cd1be3e06850edfc205dcc4fb9eb4329963..3b116835d6073ff808077d4becd83966eeacb471 100644 GIT binary patch literal 16403 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 L%n}`$8nfa*O$^;X literal 2320 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 S?6JS#9W^ytT^YXc!~X!pM3)!< literal 503 zcmV5cm&74<5h0^5xslS0D{chFX%5^Ev~TFftOQpW*-i{|p(m zo0Csoc(M?rgT>51DtYxR{~4q>Jtk*M{^3)Pr-2QyGL=c*y)v961AODw7VJCn0Ahfp ziFER=<-sHw;FqwWVE@raPy>vmlD9AQBgue(q^$)9jzPU(VI-NnZLt?g1_Y(-EI4rd z3CsZTY^EZ=CBy(17nh{xbae^N)d-hm|2L^I*ZDlh3C? zqfs<@!)!ae2K@T{kKxUGMg}%E4h9ycFAV9GCkqardI>W?Bzf&jE1U-W|MwrL`9A{- z3pc~ZPahfXJ$ld3JL^`#r5o=d2AJv#C$E`qhTQ;Qx_$TIH^ZxUe;DpR`pEG0`(Fkn zIc|pbskaJFU3?2QKu;)n^;8oK1Ar-(;qi;_4Ck(VVEFs@KZB|w4}**Z2Lm5BD?>u* z`TQei-b@D>z%0PWYGJG+7!71|q4*mZV9#EDXW-#tVc-U0HdZDECMHHuj{A20+J}|z tKKwcY%RL~Cf*{ur8H;~__yZ7s1pv)VuT8owdF}uJ002ovPDHLkV1jf++d%*T diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/screenshot/screenshot.png b/runelite-client/src/main/resources/net/runelite/client/plugins/screenshot/screenshot.png index 9a4f892bd787865f779d555255a7f7a71378e770..cae8ee2341aaeef8f823f1ae47f0a31d9fcb829a 100644 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|m7J9lkhG?AMd%=*e$v~tbaoe)* z1{I9L>TEsICXI}Gi{t3jc9s6YEpFof^VY{-dZv1E{lts5{$k3} zd3$Ev)X~50b7Yo>t<|TM&F*t~WW<~u`zJ?kcj=Rz)h6bv_l7spB%Q%c#H@#>*Cr^q zEznVW#i5qK{xp^cElC}k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5m^kh={g8AI%&+V01C2~c>21sKVlRR)-zN1^L`cs17nk?i(`n#@vBn}y_f?fTK22o zV!PC`K#3#FD`}!|#ElM#fc^y0mWbyfVu}h+*f>8jG3qaH;a<(rIH4o<)d~x-$~CiV z^XCgm&&?`N`*(Bx-J7vLz8mN*EtSft*yF5~_NCd!R*SdaUh;kz$8FF5lO`8Po#9`- zAlSD_+Gk47guu{R-KKr3jNC%?g2knZ*{4K!m&Qye`nodflza5E(92i$v&QXO-c(%9 z^~&{^M{4YOzFNlfr`(b)pDud5X5QMxyjsiIWo|VZ^Qw9}&y9R`!7A?RH4WVb$!EV> zZp*vrcj+D59OjJs55F^(%v*9-?H*IXqYuXoO(UM(Y~3TH5Sb#7y(+8b}fN|z|Fy|sC2 z#LQ;Kpx3LS!d|jp)U38nfUGhqBTq#F7WYhKG%5)7(uEft`Q|Ei6yC4$wjF^iowXh z&_dV1P}k5b#L&{p#LUXnMBBj7%D^Cb;lD~04Y~O#nQ4`{H3 Date: Tue, 1 May 2018 00:33:37 -0600 Subject: [PATCH 13/15] Cleanup FarmingTracker panels --- .../farmingtracker/FarmingPatchPanel.java | 29 +- .../farmingtracker/FarmingTrackerPanel.java | 22 +- .../plugins/farmingtracker/FarmingWorld.java | 72 +-- .../farmingtracker/PatchImplementation.java | 507 +++++++++--------- .../client/plugins/farmingtracker/Tab.java | 2 +- 5 files changed, 302 insertions(+), 330 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingPatchPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingPatchPanel.java index f2fc12b7a6..13d40259d0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingPatchPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingPatchPanel.java @@ -25,6 +25,7 @@ */ package net.runelite.client.plugins.farmingtracker; +import com.google.common.base.Strings; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; @@ -67,7 +68,7 @@ class FarmingPatchPanel extends JPanel infoPanel.setBorder(new EmptyBorder(4, 4, 4, 0)); final JLabel location = new JShadowedLabel(patch.getRegion().getName() - + (showFullTitle() ? " (" + patch.getName() + ")" : "")); + + (Strings.isNullOrEmpty(patch.getName()) ? "" : " (" + patch.getName() + ")")); location.setFont(FontManager.getRunescapeSmallFont()); location.setForeground(Color.WHITE); @@ -83,30 +84,4 @@ class FarmingPatchPanel extends JPanel add(topContainer, BorderLayout.NORTH); add(progress, BorderLayout.SOUTH); } - - /** - * This determines if the label should display location and type, ex: - * It makes sense to display: - * Catherby (North) <-- for allotment - * but not so much for herbs: - * Troll Stronghold - * as there are no other herb patches in that region. - */ - private boolean showFullTitle() - { - switch (patch.getImplementation()) - { - case FLOWER: - case HOPS: - case BUSH: - case FRUIT_TREE: - case CALQUAT: - case SPIRIT_TREE: - case TREE: - case HERB: - return false; - default: - return true; - } - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPanel.java index 77eed36352..0bd92d6838 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingTrackerPanel.java @@ -25,6 +25,7 @@ */ package net.runelite.client.plugins.farmingtracker; +import com.google.common.base.Strings; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridBagConstraints; @@ -124,15 +125,20 @@ class FarmingTrackerPanel extends PluginPanel FarmingPatchPanel p = new FarmingPatchPanel(patch); /* Show labels to subdivide tabs into sections */ - if ((tab == Tab.SPECIAL - && patch.getImplementation().ordinal() > 2 - && patch.getImplementation() != lastImpl) - || (tab == Tab.TREE - && patch.getImplementation().ordinal() > 9 - && patch.getImplementation() != lastImpl)) + if (patch.getImplementation() != lastImpl && !Strings.isNullOrEmpty(patch.getImplementation().getName())) { JLabel groupLabel = new JLabel(patch.getImplementation().getName()); - groupLabel.setBorder(new EmptyBorder(15, 0, 0, 0)); + + if (first) + { + first = false; + groupLabel.setBorder(new EmptyBorder(4, 0, 0, 0)); + } + else + { + groupLabel.setBorder(new EmptyBorder(15, 0, 0, 0)); + } + groupLabel.setFont(FontManager.getRunescapeSmallFont()); container.add(groupLabel, c); @@ -147,8 +153,8 @@ class FarmingTrackerPanel extends PluginPanel /* This is a weird hack to remove the top border on the first tracker of every tab */ if (first) { - p.setBorder(null); first = false; + p.setBorder(null); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingWorld.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingWorld.java index f4fdba1757..293b5826ef 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingWorld.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/FarmingWorld.java @@ -60,32 +60,32 @@ public class FarmingWorld )); add(new FarmingRegion("Ardougne", 10290, - new FarmingPatch("Bush", Varbits.FARMING_4771, PatchImplementation.BUSH) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) )); add(new FarmingRegion("Ardougne", 10548, new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), - new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("Herb", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) )); add(new FarmingRegion("Brimhaven", 11058, - new FarmingPatch("Fruit Tree", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE), - new FarmingPatch("Spirit Tree", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE), + new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE) )); add(new FarmingRegion("Catherby", 11062, new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), - new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("Herb", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) )); add(new FarmingRegion("Catherby", 11317, - new FarmingPatch("Fruit Tree", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) )); add(new FarmingRegion("Champion's Guild", 12596, - new FarmingPatch("Bush", Varbits.FARMING_4771, PatchImplementation.BUSH) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) )); add(new FarmingRegion("Draynor Manor", 12340, @@ -93,22 +93,22 @@ public class FarmingWorld )); add(new FarmingRegion("Entrana", 11060, - new FarmingPatch("Hops", Varbits.FARMING_4771, PatchImplementation.HOPS) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) )); add(new FarmingRegion("Etceteria", 10300, - new FarmingPatch("Bush", Varbits.FARMING_4771, PatchImplementation.BUSH), - new FarmingPatch("Spirit Tree", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH), + new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE) )); add(new FarmingRegion("Falador", 11828, - new FarmingPatch("Tree", Varbits.FARMING_4771, PatchImplementation.TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) )); add(new FarmingRegion("Falador", 12083, new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), - new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("Herb", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) ) { @Override @@ -129,23 +129,23 @@ public class FarmingWorld )); add(new FarmingRegion("Gnome Stronghold", 9781, - new FarmingPatch("Tree", Varbits.FARMING_4771, PatchImplementation.TREE), - new FarmingPatch("Fruit Tree", Varbits.FARMING_4772, PatchImplementation.FRUIT_TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE), + new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.FRUIT_TREE) )); add(new FarmingRegion("Harmony", 15148, - new FarmingPatch("Allotment", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), - new FarmingPatch("Herb", Varbits.FARMING_4772, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), + new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.HERB) )); add(new FarmingRegion("Kourend", 7222, new FarmingPatch("North East", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South West", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), - new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("Herb", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) )); add(new FarmingRegion("Kourend", 6711, - new FarmingPatch("Spirit Tree", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE) )); add(new FarmingRegion("Kourend", 7223, new FarmingPatch("West 1", Varbits.GRAPES_4953, PatchImplementation.GRAPES), @@ -163,14 +163,14 @@ public class FarmingWorld )); add(new FarmingRegion("Lletya", 9265, - new FarmingPatch("Fruit Tree", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) )); add(new FarmingRegion("Lumbridge", 12851, - new FarmingPatch("Hops", Varbits.FARMING_4771, PatchImplementation.HOPS) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) )); add(new FarmingRegion("Lumbridge", 12594, - new FarmingPatch("Tree", Varbits.FARMING_4771, PatchImplementation.TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) )); add(new FarmingRegion("Morytania", 13622, @@ -179,45 +179,45 @@ public class FarmingWorld add(new FarmingRegion("Morytania", 14391, new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), - new FarmingPatch("Flower", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("Herb", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) )); add(new FarmingRegion("Port Sarim", 12082, - new FarmingPatch("Spirit tree", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE) )); add(new FarmingRegion("Rimmington", 11570, - new FarmingPatch("Bush", Varbits.FARMING_4771, PatchImplementation.BUSH) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) ), 11826); add(new FarmingRegion("Seers' Village", 10551, - new FarmingPatch("Hops", Varbits.FARMING_4771, PatchImplementation.HOPS) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) )); add(new FarmingRegion("Tai Bwo Wannai", 11056, - new FarmingPatch("Calquat", Varbits.FARMING_4771, PatchImplementation.CALQUAT) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CALQUAT) )); add(new FarmingRegion("Taverly", 11573, - new FarmingPatch("Tree", Varbits.FARMING_4771, PatchImplementation.TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) )); add(new FarmingRegion("Tree Gnome Village", 9777, - new FarmingPatch("Fruit Tree", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) )); add(new FarmingRegion("Troll Stronghold", 11321, - new FarmingPatch("Herb", Varbits.FARMING_4771, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB) )); add(new FarmingRegion("Varrock", 12854, - new FarmingPatch("Tree", Varbits.FARMING_4771, PatchImplementation.TREE) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) ), 12853); add(new FarmingRegion("Yanille", 10288, - new FarmingPatch("Hops", Varbits.FARMING_4771, PatchImplementation.HOPS) + new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) )); // Finalize diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/PatchImplementation.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/PatchImplementation.java index bbd3a655e8..468cafd7c9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/PatchImplementation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/PatchImplementation.java @@ -31,7 +31,7 @@ import lombok.RequiredArgsConstructor; @Getter public enum PatchImplementation { - CACTUS(Tab.SPECIAL) + CACTUS(Tab.SPECIAL, "") { @Override PatchState forVarbitValue(int value) @@ -84,7 +84,7 @@ public enum PatchImplementation return null; } }, - BELLADONNA(Tab.SPECIAL) + BELLADONNA(Tab.SPECIAL, "") { @Override PatchState forVarbitValue(int value) @@ -122,7 +122,7 @@ public enum PatchImplementation return null; } }, - MUSHROOM(Tab.SPECIAL) + MUSHROOM(Tab.SPECIAL, "") { @Override PatchState forVarbitValue(int value) @@ -160,7 +160,7 @@ public enum PatchImplementation return null; } }, - ALLOTMENT(Tab.ALLOTMENT) + ALLOTMENT(Tab.ALLOTMENT, "") { @Override PatchState forVarbitValue(int value) @@ -473,240 +473,7 @@ public enum PatchImplementation return null; } }, - FLOWER(Tab.SPECIAL) - { - @Override - PatchState forVarbitValue(int value) - { - if (value >= 0 && value <= 3) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843,7842,7841,7840 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3 - value); - } - if (value >= 4 && value <= 7) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 8 && value <= 11) - { - // Marigold[,Inspect,,Guide,] 7867,7868,7869,7870 - return new PatchState(Produce.MARIGOLD, CropState.GROWING, value - 8); - } - if (value == 12) - { - // Marigold[Pick,Inspect,,Guide,] 7871 - return new PatchState(Produce.MARIGOLD, CropState.HARVESTABLE, 12 - value); - } - if (value >= 13 && value <= 16) - { - // Rosemary[,Inspect,,Guide,] 7899,7900,7901,7902 - return new PatchState(Produce.ROSEMARY, CropState.GROWING, value - 13); - } - if (value == 17) - { - // Rosemary[Pick,Inspect,,Guide,] 7903 - return new PatchState(Produce.ROSEMARY, CropState.HARVESTABLE, 17 - value); - } - if (value >= 18 && value <= 21) - { - // Nasturtium[,Inspect,,Guide,] 7883,7884,7885,7886 - return new PatchState(Produce.NASTURTIUM, CropState.GROWING, value - 18); - } - if (value == 22) - { - // Nasturtium[Pick,Inspect,,Guide,] 7887 - return new PatchState(Produce.NASTURTIUM, CropState.HARVESTABLE, 22 - value); - } - if (value >= 23 && value <= 26) - { - // Woad[,Inspect,,Guide,] 7919,7920,7921,7922 - return new PatchState(Produce.WOAD, CropState.GROWING, value - 23); - } - if (value == 27) - { - // Woad[Pick,Inspect,,Guide,] 7923 - return new PatchState(Produce.WOAD, CropState.HARVESTABLE, 27 - value); - } - if (value >= 28 && value <= 31) - { - // Limpwurt[,Inspect,,Guide,] 7851,7852,7853,7854 - return new PatchState(Produce.LIMPWURT, CropState.GROWING, value - 28); - } - if (value == 32) - { - // Limpwurt[Pick,Inspect,,Guide,] 7855 - return new PatchState(Produce.LIMPWURT, CropState.HARVESTABLE, 32 - value); - } - if (value >= 33 && value <= 35) - { - // Scarecrow[Rake,Inspect,,Guide,Remove] 7918,7917,7916 - return new PatchState(Produce.SCARECROW, CropState.GROWING, 35 - value); - } - if (value == 36) - { - // Scarecrow[Remove,Inspect,,Guide,] 7915 - return new PatchState(Produce.SCARECROW, CropState.GROWING, 0); - } - if (value >= 37 && value <= 71) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 72 && value <= 75) - { - // Marigold[,Inspect,,Guide,] 7872,7873,7874,7875 - return new PatchState(Produce.MARIGOLD, CropState.GROWING, value - 72); - } - if (value == 76) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 77 && value <= 80) - { - // Rosemary[,Inspect,,Guide,] 7904,7905,7906,7907 - return new PatchState(Produce.ROSEMARY, CropState.GROWING, value - 77); - } - if (value == 81) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 82 && value <= 85) - { - // Nasturtium[,Inspect,,Guide,] 7888,7889,7890,7891 - return new PatchState(Produce.NASTURTIUM, CropState.GROWING, value - 82); - } - if (value == 86) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 87 && value <= 90) - { - // Woad[,Inspect,,Guide,] 7924,7925,7926,7927 - return new PatchState(Produce.WOAD, CropState.GROWING, value - 87); - } - if (value == 91) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 92 && value <= 95) - { - // Limpwurt[,Inspect,,Guide,] 7856,7857,7858,7859 - return new PatchState(Produce.LIMPWURT, CropState.GROWING, value - 92); - } - if (value >= 96 && value <= 136) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 137 && value <= 139) - { - // Diseased marigold[Cure,Inspect,,Guide,] 7876,7877,7878 - return new PatchState(Produce.MARIGOLD, CropState.DISEASED, value - 136); - } - if (value >= 140 && value <= 141) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843,7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 142 && value <= 144) - { - // Diseased rosemary[Cure,Inspect,,Guide,] 7908,7909,7910 - return new PatchState(Produce.ROSEMARY, CropState.DISEASED, value - 141); - } - if (value >= 145 && value <= 146) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843,7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 147 && value <= 149) - { - // Diseased nasturtium[Cure,Inspect,,Guide,] 7892,7893,7894 - return new PatchState(Produce.NASTURTIUM, CropState.DISEASED, value - 146); - } - if (value >= 150 && value <= 151) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843,7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 152 && value <= 154) - { - // Diseased woad[Cure,Inspect,,Guide,] 7928,7929,7930 - return new PatchState(Produce.WOAD, CropState.DISEASED, value - 151); - } - if (value >= 155 && value <= 156) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843,7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 157 && value <= 159) - { - // Diseased limpwurt[Cure,Inspect,,Guide,] 7860,7861,7862 - return new PatchState(Produce.LIMPWURT, CropState.DISEASED, value - 156); - } - if (value >= 160 && value <= 200) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 201 && value <= 203) - { - // Dead marigold[Clear,Inspect,,Guide,] 7879,7880,7881,7882 - return new PatchState(Produce.MARIGOLD, CropState.DEAD, value - 200); - } - if (value == 205) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 206 && value <= 208) - { - // Dead rosemary[Clear,Inspect,,Guide,] 7911,7912,7913,7914 - return new PatchState(Produce.ROSEMARY, CropState.DEAD, value - 205); - } - if (value == 210) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 211 && value <= 213) - { - // Dead nasturtium[Clear,Inspect,,Guide,] 7895,7896,7897,7898 - return new PatchState(Produce.NASTURTIUM, CropState.DEAD, value - 210); - } - if (value == 215) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 216 && value <= 218) - { - // Dead woad[Clear,Inspect,,Guide,] 7931,7932,7933,7934 - return new PatchState(Produce.WOAD, CropState.DEAD, value - 215); - } - if (value == 220) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - if (value >= 221 && value <= 224) - { - // Dead limpwurt[Clear,Inspect,,Guide,] 7863,7864,7865,7866 - return new PatchState(Produce.LIMPWURT, CropState.DEAD, value - 220); - } - if (value >= 225 && value <= 255) - { - // Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843 - return new PatchState(Produce.WEEDS, CropState.GROWING, 3); - } - return null; - } - }, - HERB(Tab.HERB) + HERB(Tab.HERB, "") { @Override PatchState forVarbitValue(int value) @@ -970,7 +737,240 @@ public enum PatchImplementation return null; } }, - BUSH(Tab.BUSH) + FLOWER(Tab.HERB, "Flowers") + { + @Override + PatchState forVarbitValue(int value) + { + if (value >= 0 && value <= 3) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843,7842,7841,7840 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3 - value); + } + if (value >= 4 && value <= 7) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 8 && value <= 11) + { + // Marigold[,Inspect,,Guide,] 7867,7868,7869,7870 + return new PatchState(Produce.MARIGOLD, CropState.GROWING, value - 8); + } + if (value == 12) + { + // Marigold[Pick,Inspect,,Guide,] 7871 + return new PatchState(Produce.MARIGOLD, CropState.HARVESTABLE, 12 - value); + } + if (value >= 13 && value <= 16) + { + // Rosemary[,Inspect,,Guide,] 7899,7900,7901,7902 + return new PatchState(Produce.ROSEMARY, CropState.GROWING, value - 13); + } + if (value == 17) + { + // Rosemary[Pick,Inspect,,Guide,] 7903 + return new PatchState(Produce.ROSEMARY, CropState.HARVESTABLE, 17 - value); + } + if (value >= 18 && value <= 21) + { + // Nasturtium[,Inspect,,Guide,] 7883,7884,7885,7886 + return new PatchState(Produce.NASTURTIUM, CropState.GROWING, value - 18); + } + if (value == 22) + { + // Nasturtium[Pick,Inspect,,Guide,] 7887 + return new PatchState(Produce.NASTURTIUM, CropState.HARVESTABLE, 22 - value); + } + if (value >= 23 && value <= 26) + { + // Woad[,Inspect,,Guide,] 7919,7920,7921,7922 + return new PatchState(Produce.WOAD, CropState.GROWING, value - 23); + } + if (value == 27) + { + // Woad[Pick,Inspect,,Guide,] 7923 + return new PatchState(Produce.WOAD, CropState.HARVESTABLE, 27 - value); + } + if (value >= 28 && value <= 31) + { + // Limpwurt[,Inspect,,Guide,] 7851,7852,7853,7854 + return new PatchState(Produce.LIMPWURT, CropState.GROWING, value - 28); + } + if (value == 32) + { + // Limpwurt[Pick,Inspect,,Guide,] 7855 + return new PatchState(Produce.LIMPWURT, CropState.HARVESTABLE, 32 - value); + } + if (value >= 33 && value <= 35) + { + // Scarecrow[Rake,Inspect,,Guide,Remove] 7918,7917,7916 + return new PatchState(Produce.SCARECROW, CropState.GROWING, 35 - value); + } + if (value == 36) + { + // Scarecrow[Remove,Inspect,,Guide,] 7915 + return new PatchState(Produce.SCARECROW, CropState.GROWING, 0); + } + if (value >= 37 && value <= 71) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 72 && value <= 75) + { + // Marigold[,Inspect,,Guide,] 7872,7873,7874,7875 + return new PatchState(Produce.MARIGOLD, CropState.GROWING, value - 72); + } + if (value == 76) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 77 && value <= 80) + { + // Rosemary[,Inspect,,Guide,] 7904,7905,7906,7907 + return new PatchState(Produce.ROSEMARY, CropState.GROWING, value - 77); + } + if (value == 81) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 82 && value <= 85) + { + // Nasturtium[,Inspect,,Guide,] 7888,7889,7890,7891 + return new PatchState(Produce.NASTURTIUM, CropState.GROWING, value - 82); + } + if (value == 86) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 87 && value <= 90) + { + // Woad[,Inspect,,Guide,] 7924,7925,7926,7927 + return new PatchState(Produce.WOAD, CropState.GROWING, value - 87); + } + if (value == 91) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 92 && value <= 95) + { + // Limpwurt[,Inspect,,Guide,] 7856,7857,7858,7859 + return new PatchState(Produce.LIMPWURT, CropState.GROWING, value - 92); + } + if (value >= 96 && value <= 136) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 137 && value <= 139) + { + // Diseased marigold[Cure,Inspect,,Guide,] 7876,7877,7878 + return new PatchState(Produce.MARIGOLD, CropState.DISEASED, value - 136); + } + if (value >= 140 && value <= 141) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843,7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 142 && value <= 144) + { + // Diseased rosemary[Cure,Inspect,,Guide,] 7908,7909,7910 + return new PatchState(Produce.ROSEMARY, CropState.DISEASED, value - 141); + } + if (value >= 145 && value <= 146) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843,7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 147 && value <= 149) + { + // Diseased nasturtium[Cure,Inspect,,Guide,] 7892,7893,7894 + return new PatchState(Produce.NASTURTIUM, CropState.DISEASED, value - 146); + } + if (value >= 150 && value <= 151) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843,7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 152 && value <= 154) + { + // Diseased woad[Cure,Inspect,,Guide,] 7928,7929,7930 + return new PatchState(Produce.WOAD, CropState.DISEASED, value - 151); + } + if (value >= 155 && value <= 156) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843,7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 157 && value <= 159) + { + // Diseased limpwurt[Cure,Inspect,,Guide,] 7860,7861,7862 + return new PatchState(Produce.LIMPWURT, CropState.DISEASED, value - 156); + } + if (value >= 160 && value <= 200) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 201 && value <= 203) + { + // Dead marigold[Clear,Inspect,,Guide,] 7879,7880,7881,7882 + return new PatchState(Produce.MARIGOLD, CropState.DEAD, value - 200); + } + if (value == 205) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 206 && value <= 208) + { + // Dead rosemary[Clear,Inspect,,Guide,] 7911,7912,7913,7914 + return new PatchState(Produce.ROSEMARY, CropState.DEAD, value - 205); + } + if (value == 210) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 211 && value <= 213) + { + // Dead nasturtium[Clear,Inspect,,Guide,] 7895,7896,7897,7898 + return new PatchState(Produce.NASTURTIUM, CropState.DEAD, value - 210); + } + if (value == 215) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 216 && value <= 218) + { + // Dead woad[Clear,Inspect,,Guide,] 7931,7932,7933,7934 + return new PatchState(Produce.WOAD, CropState.DEAD, value - 215); + } + if (value == 220) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + if (value >= 221 && value <= 224) + { + // Dead limpwurt[Clear,Inspect,,Guide,] 7863,7864,7865,7866 + return new PatchState(Produce.LIMPWURT, CropState.DEAD, value - 220); + } + if (value >= 225 && value <= 255) + { + // Flower Patch[Rake,Inspect,,Guide,] 7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843,7843 + return new PatchState(Produce.WEEDS, CropState.GROWING, 3); + } + return null; + } + }, + BUSH(Tab.BUSH, "") { @Override PatchState forVarbitValue(int value) @@ -1209,7 +1209,7 @@ public enum PatchImplementation return null; } }, - FRUIT_TREE(Tab.FRUIT_TREE) + FRUIT_TREE(Tab.FRUIT_TREE, "Fruit trees") { @Override PatchState forVarbitValue(int value) @@ -1458,7 +1458,7 @@ public enum PatchImplementation return null; } }, - HOPS(Tab.SPECIAL) + HOPS(Tab.SPECIAL, "Hops") { @Override PatchState forVarbitValue(int value) @@ -1761,7 +1761,7 @@ public enum PatchImplementation return null; } }, - TREE(Tab.TREE) + TREE(Tab.TREE, "") { @Override PatchState forVarbitValue(int value) @@ -2049,7 +2049,7 @@ public enum PatchImplementation return null; } }, - HARDWOOD_TREE(Tab.TREE) + HARDWOOD_TREE(Tab.TREE, "Hardwood Trees") { @Override PatchState forVarbitValue(int value) @@ -2137,7 +2137,7 @@ public enum PatchImplementation return null; } }, - SPIRIT_TREE(Tab.TREE) + SPIRIT_TREE(Tab.TREE, "Spirit Trees") { @Override PatchState forVarbitValue(int value) @@ -2185,7 +2185,7 @@ public enum PatchImplementation return null; } }, - SEAWEED(Tab.SPECIAL) + SEAWEED(Tab.SPECIAL, "Seaweed") { @Override PatchState forVarbitValue(int value) @@ -2223,7 +2223,7 @@ public enum PatchImplementation return null; } }, - CALQUAT(Tab.FRUIT_TREE) + CALQUAT(Tab.FRUIT_TREE, "Calquat") { @Override PatchState forVarbitValue(int value) @@ -2271,7 +2271,7 @@ public enum PatchImplementation return null; } }, - GRAPES(Tab.SPECIAL) + GRAPES(Tab.BUSH, "Grapes") { @Override PatchState forVarbitValue(int value) @@ -2299,16 +2299,7 @@ public enum PatchImplementation abstract PatchState forVarbitValue(int value); - /** - * Returns a formatted name based on the enum's name. - * Ex: - * SPIRIT_TREE into Spirit Tree - * HERB into Herb - */ - public String getName() - { - return (name().substring(0, 1).toUpperCase() + name().toLowerCase().substring(1)).replaceAll("_", " "); - } - private final Tab tab; + + private final String name; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/Tab.java b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/Tab.java index b8838742dc..50f50b2ed8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/Tab.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/farmingtracker/Tab.java @@ -36,7 +36,7 @@ public enum Tab HERB("Herbs", ItemID.GRIMY_RANARR_WEED), TREE("Trees", ItemID.MAHOGANY_LOGS), FRUIT_TREE("Fruit Trees", ItemID.PINEAPPLE), - BUSH("Bushes", ItemID.WHITE_BERRIES), + BUSH("Bushes", ItemID.REDBERRIES), SPECIAL("Special", ItemID.MUSHROOM); private final String name; From ae617f019d233d5fec5583ca3cc236afc7e165b0 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 8 May 2018 21:57:40 -0400 Subject: [PATCH 14/15] Update substance to 8.0.02 --- runelite-client/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index cb8af2b1a3..0fec995f70 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -77,12 +77,12 @@ net.runelite.pushingpixels substance - 8.0.00-dev + 8.0.02 net.runelite.pushingpixels trident - 1.4 + 1.5.00 runtime From 23395b6d48bddf5873ea63d718940c8e48d58437 Mon Sep 17 00:00:00 2001 From: Ruben Amendoeira Date: Sat, 19 May 2018 14:24:43 +0100 Subject: [PATCH 15/15] Skilling Calculators Plugin design tweak Prepared it for release, still to be redesigned. Changes: - Changed the background color on some panels to match the new ui - Re-ordered the plugin to a lower position on the side bar --- .../plugins/skillcalculator/SkillCalculatorPanel.java | 4 ++++ .../plugins/skillcalculator/SkillCalculatorPlugin.java | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java index 25b2797c3d..b0e520a33e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java @@ -38,6 +38,7 @@ import javax.swing.JButton; import javax.swing.JPanel; import net.runelite.api.Client; import net.runelite.client.game.SkillIconManager; +import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; class SkillCalculatorPanel extends PluginPanel @@ -64,10 +65,13 @@ class SkillCalculatorPanel extends PluginPanel uiButtonGridConstraints.insets = new Insets(2, 2, 2, 2); uiButtonGrid.setLayout(uiButtonGridLayout); + uiButtonGrid.setBackground(ColorScheme.DARK_GRAY_COLOR); uiButtonGrid.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); addCalculatorButtons(); final UICalculatorInputArea uiInput = new UICalculatorInputArea(); + uiInput.setBackground(ColorScheme.DARK_GRAY_COLOR); + uiCalculator = new SkillCalculator(client, uiInput); add(uiButtonGrid); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java index 66bfbc70c0..df124975a6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java @@ -75,7 +75,12 @@ public class SkillCalculatorPlugin extends Plugin SkillCalculator.itemManager = itemManager; uiPanel = new SkillCalculatorPanel(skillIconManager, client); - uiNavigationButton = NavigationButton.builder().name("Skill Calculator").icon(icon).panel(uiPanel).build(); + uiNavigationButton = NavigationButton.builder() + .name("Skill Calculator") + .icon(icon) + .priority(6) + .panel(uiPanel) + .build(); pluginToolbar.addNavigation(uiNavigationButton); }