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 2f4631d36e..bf70440802 100644 Binary files a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/hardcore_ironman.png and b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/hardcore_ironman.png differ 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 a00b92b1f8..453e0326de 100644 Binary files a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/ironman.png and b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/ironman.png differ 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 c00f4ec040..375dd4e906 100644 Binary files a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/normal.png and b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/normal.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/search.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/search.png deleted file mode 100644 index 23bf66b759..0000000000 Binary files a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/search.png and /dev/null differ 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 efa7643d97..07eb519971 100644 Binary files a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/ultimate_ironman.png and b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/ultimate_ironman.png differ