From 802e7f628d8890edc4b80d56af62f2b1a59bfaf5 Mon Sep 17 00:00:00 2001 From: mcscoobe Date: Tue, 23 Dec 2025 01:25:08 -0500 Subject: [PATCH 1/2] Tick change shortcut feature - Add new tick configuration called Tick Settings - Hotkeys which is in addition to the pre-existing Tick Settings 1 to 3. - Add two hotkeys under the new Tick Setting that can increment or decrement the total ticks - If decrement is fired when tick counter is at its minimum (two) then the render will be in a disabled state until it is incremented again. - Changing ticks with hotkeys will reflect in the configuration side panel, but if the panel is open when changes are made a refresh is needed for them to be seen. --- .../visualticks/BaseVisualTicksOverlay.java | 7 +- .../com/visualticks/VisualTicksConfig.java | 217 ++++++++++++++ .../VisualTicksOverlayHotkeys.java | 98 +++++++ .../com/visualticks/VisualTicksPlugin.java | 225 ++++++++++++++- .../visualticks/VisualTicksPluginTest.java | 266 ++++++++++++++++++ 5 files changed, 811 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/visualticks/VisualTicksOverlayHotkeys.java diff --git a/src/main/java/com/visualticks/BaseVisualTicksOverlay.java b/src/main/java/com/visualticks/BaseVisualTicksOverlay.java index 5d7a419..c2129a5 100644 --- a/src/main/java/com/visualticks/BaseVisualTicksOverlay.java +++ b/src/main/java/com/visualticks/BaseVisualTicksOverlay.java @@ -111,13 +111,18 @@ public Dimension render(Graphics2D graphics) } if(getExclusiveTab().getIndex() != -1 && client.getVarcIntValue(VarClientInt.INVENTORY_TAB) != getExclusiveTab().getIndex()) return null; - if(ticks.size() < getNumberOfTicks() - 1) return null; + if(ticks.size() < getNumberOfTicks()) return null; Font originalFont = graphics.getFont(); graphics.setFont(graphics.getFont().deriveFont((float) getTickTextSize())); for (int i = 0; i < getNumberOfTicks(); i++) { + // Additional safety check to prevent IndexOutOfBoundsException + if (i >= ticks.size()) { + break; + } + Tick tick = ticks.get(i); if (shouldShowTickShape()) { graphics.setColor(i == getCurrentTick() ? getCurrentTickColour() : getTickColour()); diff --git a/src/main/java/com/visualticks/VisualTicksConfig.java b/src/main/java/com/visualticks/VisualTicksConfig.java index d790129..1d00d9f 100644 --- a/src/main/java/com/visualticks/VisualTicksConfig.java +++ b/src/main/java/com/visualticks/VisualTicksConfig.java @@ -613,4 +613,221 @@ default int verticalSpacingThree() { return 5; } //endregion + + //region Tick settings - Hotkeys + @ConfigSection( + name = "Tick settings - Hotkeys", + description = "Settings for the hotkey-controlled tick configuration", + position = 4 + ) + String tickSettingsHotkeys = "tickSettingsHotkeys"; + + @ConfigItem( + keyName = "isEnabledHotkeys", + name = "Enabled", + description = "Enable the hotkey-controlled tick configuration", + section = tickSettingsHotkeys, + position = 0 + ) + default boolean isEnabledHotkeys() { + return false; + } + + @ConfigItem( + keyName = "shouldShowTickShapeHotkeys", + name = "Show Tick Shape", + description = "Show the shape of the hotkey-controlled ticks", + section = tickSettingsHotkeys, + position = 1 + ) + default boolean shouldShowTickShapeHotkeys() { + return true; + } + + @ConfigItem( + keyName = "tickShapeHotkeys", + name = "Tick shape", + description = "The shape of the ticks", + section = tickSettingsHotkeys, + position = 2 + ) + default TickShape tickShapeHotkeys() { + return TickShape.CIRCLE; + } + + @ConfigItem( + keyName = "exclusiveTabHotkeys", + name = "Only show if on tab", + description = "Show the ticks only when the selected tab is active", + section = tickSettingsHotkeys, + position = 3 + ) + default InterfaceTab exclusiveTabHotkeys() { + return InterfaceTab.ALL; + } + + @ConfigItem( + keyName = "numberOfTicksHotkeys", + name = "Number of ticks", + description = "Number of tick circles to display", + section = tickSettingsHotkeys, + position = 4 + ) + @Range(min = 2, max = 30) + default int numberOfTicksHotkeys() { + return 2; + } + + @ConfigItem( + keyName = "amountPerRowHotkeys", + name = "Amount per row", + description = "How many ticks to display per row", + section = tickSettingsHotkeys, + position = 5 + ) + @Range(min = 1) + default int amountPerRowHotkeys() { + return 8; + } + + @ConfigItem( + keyName = "tickColourHotkeys", + name = "Tick colour", + description = "The colour of the ticks", + section = tickSettingsHotkeys, + position = 6 + ) + @Alpha + default Color tickColourHotkeys() { + return new Color(41, 128, 185); + } + + @ConfigItem( + keyName = "currentTickColourHotkeys", + name = "Current tick colour", + description = "The colour of the current tick", + section = tickSettingsHotkeys, + position = 7 + ) + @Alpha + default Color currentTickColourHotkeys() { + return new Color(236, 240, 241); + } + + @ConfigItem( + keyName = "sizeOfTickShapesHotkeys", + name = "Size of ticks", + description = "How many pixels to make the tick shapes", + section = tickSettingsHotkeys, + position = 8 + ) + default int sizeOfTickShapesHotkeys() { + return 32; + } + + @ConfigItem( + keyName = "tickArcHotkeys", + name = "Tick arc(rounded square)", + description = "The arc of the corners of the rounded square tick shape", + section = tickSettingsHotkeys, + position = 9 + ) + @Range(max = 100) + default int tickArcHotkeys() { + return 10; + } + + @ConfigItem( + keyName = "shouldShowTextHotkeys", + name = "Show text", + description = "Show the text of the current tick", + section = tickSettingsHotkeys, + position = 10 + ) + default boolean shouldShowTextHotkeys() { + return true; + } + + @ConfigItem( + keyName = "tickTextSizeHotkeys", + name = "Tick text size", + description = "The size of the text on the ticks", + section = tickSettingsHotkeys, + position = 11 + ) + @Range(min = 1, max = 100) + default int tickTextSizeHotkeys() { + return 15; + } + + @ConfigItem( + keyName = "tickTextColourHotkeys", + name = "Tick text colour", + description = "The colour of the text non-current ticks", + section = tickSettingsHotkeys, + position = 12 + ) + @Alpha + default Color tickTextColourHotkeys() { + return new Color(236, 240, 241); + } + + @ConfigItem( + keyName = "currentTickTextColourHotkeys", + name = "Current tick text colour", + description = "The colour of the text on the current tick", + section = tickSettingsHotkeys, + position = 13 + ) + @Alpha + default Color currentTickTextColourHotkeys() { + return new Color(41, 128, 185); + } + + @ConfigItem( + keyName = "horizontalSpacingHotkeys", + name = "Horizontal spacing", + description = "The amount of space between ticks on the x-axis", + section = tickSettingsHotkeys, + position = 14 + ) + @Range(min = -50) + default int horizontalSpacingHotkeys() { + return 5; + } + + @ConfigItem( + keyName = "verticalSpacingHotkeys", + name = "Vertical spacing", + description = "The amount of space between ticks on the y-axis", + section = tickSettingsHotkeys, + position = 15 + ) + @Range(min = -50) + default int verticalSpacingHotkeys() { + return 5; + } + + @ConfigItem( + position = 16, + keyName = "tickIncrementHotkey", + name = "Tick Increment Hotkey", + description = "Hotkey to increment number of ticks", + section = tickSettingsHotkeys + ) + default Keybind tickIncrementHotkey() { + return new Keybind(KeyEvent.VK_EQUALS, KeyEvent.VK_UNDEFINED); + } + + @ConfigItem( + position = 17, + keyName = "tickDecrementHotkey", + name = "Tick Decrement Hotkey", + description = "Hotkey to decrement number of ticks", + section = tickSettingsHotkeys + ) + default Keybind tickDecrementHotkey() { + return new Keybind(KeyEvent.VK_MINUS, KeyEvent.VK_UNDEFINED); + } + //endregion } diff --git a/src/main/java/com/visualticks/VisualTicksOverlayHotkeys.java b/src/main/java/com/visualticks/VisualTicksOverlayHotkeys.java new file mode 100644 index 0000000..74434bc --- /dev/null +++ b/src/main/java/com/visualticks/VisualTicksOverlayHotkeys.java @@ -0,0 +1,98 @@ +package com.visualticks; + +import com.visualticks.config.InterfaceTab; +import com.visualticks.config.TickShape; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import javax.inject.Inject; +import java.awt.*; + +@Slf4j +public class VisualTicksOverlayHotkeys extends BaseVisualTicksOverlay +{ + @Inject + public VisualTicksOverlayHotkeys(VisualTicksPlugin plugin, VisualTicksConfig config, Client client) + { + super(plugin, config, client); + } + + @Override + protected boolean shouldShowText() { + return config.shouldShowTextHotkeys(); + } + + @Override + protected boolean shouldShowTickShape() { + return config.shouldShowTickShapeHotkeys(); + } + + @Override + protected int getTickTextSize() { + return config.tickTextSizeHotkeys(); + } + + @Override + protected int getNumberOfTicks() { + return config.numberOfTicksHotkeys(); + } + + @Override + protected Color getTickColour() { + return config.tickColourHotkeys(); + } + + @Override + protected Color getCurrentTickColour() { + return config.currentTickColourHotkeys(); + } + + @Override + protected int getAmountPerRow() { + return config.amountPerRowHotkeys(); + } + + @Override + protected int getSizeOfTickShapes() { + return config.sizeOfTickShapesHotkeys(); + } + + @Override + protected int getHorizontalSpacing() { + return config.horizontalSpacingHotkeys(); + } + + @Override + protected int getVerticalSpacing() { + return config.verticalSpacingHotkeys(); + } + + @Override + protected int getCurrentTick() { + return plugin.tickHotkeys; + } + + @Override + protected InterfaceTab getExclusiveTab() { + return config.exclusiveTabHotkeys(); + } + + @Override + protected Color getTickTextColour() { + return config.tickTextColourHotkeys(); + } + + @Override + protected Color getCurrentTickTextColour() { + return config.currentTickTextColourHotkeys(); + } + + @Override + protected TickShape getTickShape() { + return config.tickShapeHotkeys(); + } + + @Override + protected int getTickArc() { + return config.tickArcHotkeys(); + } +} \ No newline at end of file diff --git a/src/main/java/com/visualticks/VisualTicksPlugin.java b/src/main/java/com/visualticks/VisualTicksPlugin.java index 6aacbc9..bcb7310 100644 --- a/src/main/java/com/visualticks/VisualTicksPlugin.java +++ b/src/main/java/com/visualticks/VisualTicksPlugin.java @@ -31,12 +31,15 @@ public class VisualTicksPlugin extends Plugin implements KeyListener { @Inject private VisualTicksOverlayThree overlayThree; @Inject + private VisualTicksOverlayHotkeys overlayHotkeys; + @Inject private KeyManager keyManager; @Inject private ConfigManager configManager; public int tickOne = 0; public int tickTwo = 0; public int tickThree = 0; + public int tickHotkeys = 0; @Override protected void startUp() throws Exception { @@ -50,12 +53,13 @@ protected void shutDown() throws Exception { overlayManager.remove(overlayOne); overlayManager.remove(overlayTwo); overlayManager.remove(overlayThree); + overlayManager.remove(overlayHotkeys); keyManager.unregisterKeyListener(this); } @Subscribe private void onGameTick(GameTick gameTick) { - if (!config.isEnabledOne() && !config.isEnabledTwo() && !config.isEnabledThree()) { + if (!config.isEnabledOne() && !config.isEnabledTwo() && !config.isEnabledThree() && !config.isEnabledHotkeys()) { return; } @@ -70,6 +74,10 @@ private void onGameTick(GameTick gameTick) { if(config.isEnabledThree()) { tickThree = (tickThree + 1) % config.numberOfTicksThree(); } + + if(config.isEnabledHotkeys()) { + tickHotkeys = (tickHotkeys + 1) % config.numberOfTicksHotkeys(); + } } @Subscribe @@ -78,6 +86,19 @@ private void onConfigChanged(ConfigChanged event) { return; } + // Handle hotkey configuration changes + String key = event.getKey(); + if (isHotkeyConfigurationKey(key)) { + // Re-register hotkeys with KeyManager when hotkey configs change + keyManager.unregisterKeyListener(this); + keyManager.registerKeyListener(this); + } + + // Handle numberOfTicks changes with proper tick value adjustment + if (isNumberOfTicksConfigurationKey(key)) { + adjustTickValuesForConfigurationChange(key); + } + updateOverlays(); } @@ -85,6 +106,7 @@ private void updateOverlays() { overlayManager.remove(overlayOne); overlayManager.remove(overlayTwo); overlayManager.remove(overlayThree); + overlayManager.remove(overlayHotkeys); if (config.isEnabledOne()) { overlayManager.add(overlayOne); @@ -95,10 +117,14 @@ private void updateOverlays() { if (config.isEnabledThree()) { overlayManager.add(overlayThree); } + if (config.isEnabledHotkeys()) { + overlayManager.add(overlayHotkeys); + } overlayOne.onConfigChanged(); overlayTwo.onConfigChanged(); overlayThree.onConfigChanged(); + overlayHotkeys.onConfigChanged(); } @Subscribe @@ -118,8 +144,71 @@ public void keyTyped(KeyEvent e) { @Override public void keyPressed(KeyEvent e) { + // Existing tick reset functionality if (config.tickResetHotkey().matches(e)) { resetTicks(); + return; + } + + // Handle increment/decrement hotkeys for hotkey configuration only + handleIncrementDecrementKeys(e); + } + + /** + * Handles increment and decrement hotkey presses for the hotkey configuration. + * Increments/decrements the numberOfTicksHotkeys configuration value. + * + * @param e The KeyEvent to process + */ + private void handleIncrementDecrementKeys(KeyEvent e) { + // Hotkey configuration increment/decrement for numberOfTicks + if (config.tickIncrementHotkey().matches(e)) { + incrementNumberOfTicks(); + return; + } + if (config.tickDecrementHotkey().matches(e)) { + decrementNumberOfTicks(); + return; + } + } + + /** + * Increments the numberOfTicksHotkeys configuration value. + * If the Hotkeys configuration is disabled, enables it and sets to 2 ticks. + * Otherwise increments up to maximum of 30. + */ + private void incrementNumberOfTicks() { + if (!config.isEnabledHotkeys()) { + // If disabled, enable it and set to minimum (2 ticks) + configManager.setConfiguration(VisualTicksConfig.GROUP_NAME, "isEnabledHotkeys", true); + configManager.setConfiguration(VisualTicksConfig.GROUP_NAME, "numberOfTicksHotkeys", 2); + } else { + // If enabled, increment up to maximum + int currentValue = config.numberOfTicksHotkeys(); + if (currentValue < 30) { // Max value from @Range annotation + configManager.setConfiguration(VisualTicksConfig.GROUP_NAME, "numberOfTicksHotkeys", currentValue + 1); + } + } + } + + /** + * Decrements the numberOfTicksHotkeys configuration value. + * If already at minimum (2 ticks), disables the Hotkeys configuration. + * Otherwise decrements down to minimum of 2. + */ + private void decrementNumberOfTicks() { + if (!config.isEnabledHotkeys()) { + // If already disabled, do nothing + return; + } + + int currentValue = config.numberOfTicksHotkeys(); + if (currentValue <= 2) { + // If at minimum, disable the configuration + configManager.setConfiguration(VisualTicksConfig.GROUP_NAME, "isEnabledHotkeys", false); + } else { + // Otherwise decrement normally + configManager.setConfiguration(VisualTicksConfig.GROUP_NAME, "numberOfTicksHotkeys", currentValue - 1); } } @@ -154,5 +243,139 @@ private void resetTicks() { tickOne = 0; tickTwo = 0; tickThree = 0; + tickHotkeys = 0; + } + + /** + * Increments the tick count for the specified configuration. + * Uses modulo arithmetic to wrap around when reaching the maximum tick count. + * Only operates on enabled configurations. + * + * @param configurationNumber The configuration to increment (1, 2, 3, or 4) + */ + private void incrementTick(int configurationNumber) { + if (!isConfigurationEnabled(configurationNumber)) { + return; + } + + switch (configurationNumber) { + case 1: + tickOne = (tickOne + 1) % config.numberOfTicksOne(); + break; + case 2: + tickTwo = (tickTwo + 1) % config.numberOfTicksTwo(); + break; + case 3: + tickThree = (tickThree + 1) % config.numberOfTicksThree(); + break; + case 4: + tickHotkeys = (tickHotkeys + 1) % config.numberOfTicksHotkeys(); + break; + } + } + + /** + * Decrements the tick count for the specified configuration. + * Uses modulo arithmetic to wrap around when going below zero. + * Only operates on enabled configurations. + * + * @param configurationNumber The configuration to decrement (1, 2, 3, or 4) + */ + private void decrementTick(int configurationNumber) { + if (!isConfigurationEnabled(configurationNumber)) { + return; + } + + switch (configurationNumber) { + case 1: + tickOne = (tickOne - 1 + config.numberOfTicksOne()) % config.numberOfTicksOne(); + break; + case 2: + tickTwo = (tickTwo - 1 + config.numberOfTicksTwo()) % config.numberOfTicksTwo(); + break; + case 3: + tickThree = (tickThree - 1 + config.numberOfTicksThree()) % config.numberOfTicksThree(); + break; + case 4: + tickHotkeys = (tickHotkeys - 1 + config.numberOfTicksHotkeys()) % config.numberOfTicksHotkeys(); + break; + } + } + + /** + * Helper method to check if a configuration is enabled before manipulation. + * + * @param configurationNumber The configuration to check (1, 2, 3, or 4) + * @return true if the configuration is enabled, false otherwise + */ + private boolean isConfigurationEnabled(int configurationNumber) { + switch (configurationNumber) { + case 1: + return config.isEnabledOne(); + case 2: + return config.isEnabledTwo(); + case 3: + return config.isEnabledThree(); + case 4: + return config.isEnabledHotkeys(); + default: + return false; + } + } + + /** + * Checks if the given configuration key is a hotkey configuration. + * + * @param key The configuration key to check + * @return true if the key is a hotkey configuration, false otherwise + */ + private boolean isHotkeyConfigurationKey(String key) { + return "tickResetHotkey".equals(key) || + "tickIncrementHotkey".equals(key) || + "tickDecrementHotkey".equals(key); + } + + /** + * Checks if the given configuration key is a numberOfTicks configuration. + * + * @param key The configuration key to check + * @return true if the key is a numberOfTicks configuration, false otherwise + */ + private boolean isNumberOfTicksConfigurationKey(String key) { + return "numberOfTicksOne".equals(key) || + "numberOfTicksTwo".equals(key) || + "numberOfTicksThree".equals(key) || + "numberOfTicksHotkeys".equals(key); + } + + /** + * Adjusts tick values when numberOfTicks configuration changes. + * Uses modulo operation to ensure tick values remain within valid range. + * + * @param key The configuration key that changed + */ + private void adjustTickValuesForConfigurationChange(String key) { + switch (key) { + case "numberOfTicksOne": + if (config.numberOfTicksOne() > 0) { + tickOne = tickOne % config.numberOfTicksOne(); + } + break; + case "numberOfTicksTwo": + if (config.numberOfTicksTwo() > 0) { + tickTwo = tickTwo % config.numberOfTicksTwo(); + } + break; + case "numberOfTicksThree": + if (config.numberOfTicksThree() > 0) { + tickThree = tickThree % config.numberOfTicksThree(); + } + break; + case "numberOfTicksHotkeys": + if (config.numberOfTicksHotkeys() > 0) { + tickHotkeys = tickHotkeys % config.numberOfTicksHotkeys(); + } + break; + } } } diff --git a/src/test/java/com/visualticks/VisualTicksPluginTest.java b/src/test/java/com/visualticks/VisualTicksPluginTest.java index a77bd18..24bc91d 100644 --- a/src/test/java/com/visualticks/VisualTicksPluginTest.java +++ b/src/test/java/com/visualticks/VisualTicksPluginTest.java @@ -2,9 +2,275 @@ import net.runelite.client.RuneLite; import net.runelite.client.externalplugins.ExternalPluginManager; +import org.junit.Test; +import org.junit.Before; +import net.runelite.api.Client; + +import static org.junit.Assert.*; public class VisualTicksPluginTest { + private VisualTicksPlugin plugin; + private TestConfig testConfig; + + /** + * Simple test configuration implementation for testing overlay behavior. + */ + private static class TestConfig implements VisualTicksConfig { + private int numberOfTicksOne = 5; + private int numberOfTicksTwo = 3; + private int numberOfTicksThree = 4; + private int numberOfTicksHotkeys = 6; + private boolean enabledOne = true; + private boolean enabledTwo = true; + private boolean enabledThree = true; + private boolean enabledHotkeys = true; + + @Override public int numberOfTicksOne() { return numberOfTicksOne; } + @Override public int numberOfTicksTwo() { return numberOfTicksTwo; } + @Override public int numberOfTicksThree() { return numberOfTicksThree; } + @Override public int numberOfTicksHotkeys() { return numberOfTicksHotkeys; } + @Override public boolean isEnabledOne() { return enabledOne; } + @Override public boolean isEnabledTwo() { return enabledTwo; } + @Override public boolean isEnabledThree() { return enabledThree; } + @Override public boolean isEnabledHotkeys() { return enabledHotkeys; } + + public void setEnabledOne(boolean enabled) { this.enabledOne = enabled; } + public void setEnabledTwo(boolean enabled) { this.enabledTwo = enabled; } + + // Default implementations for other required methods + @Override public net.runelite.client.config.Keybind tickResetHotkey() { return null; } + @Override public net.runelite.client.config.Keybind tickIncrementHotkey() { return null; } + @Override public net.runelite.client.config.Keybind tickDecrementHotkey() { return null; } + @Override public boolean shouldShowTickShapeOne() { return true; } + @Override public com.visualticks.config.TickShape tickShapeOne() { return com.visualticks.config.TickShape.CIRCLE; } + @Override public com.visualticks.config.InterfaceTab exclusiveTabOne() { return com.visualticks.config.InterfaceTab.ALL; } + @Override public int amountPerRowOne() { return 8; } + @Override public java.awt.Color tickColourOne() { return java.awt.Color.BLUE; } + @Override public java.awt.Color currentTickColourOne() { return java.awt.Color.WHITE; } + @Override public int sizeOfTickShapesOne() { return 32; } + @Override public int tickArcOne() { return 10; } + @Override public boolean shouldShowTextOne() { return true; } + @Override public int tickTextSizeOne() { return 15; } + @Override public java.awt.Color tickTextColourOne() { return java.awt.Color.WHITE; } + @Override public java.awt.Color currentTickTextColourOne() { return java.awt.Color.BLUE; } + @Override public int horizontalSpacingOne() { return 5; } + @Override public int verticalSpacingOne() { return 5; } + @Override public boolean shouldShowTickShapeTwo() { return true; } + @Override public com.visualticks.config.TickShape tickShapeTwo() { return com.visualticks.config.TickShape.CIRCLE; } + @Override public com.visualticks.config.InterfaceTab exclusiveTabTwo() { return com.visualticks.config.InterfaceTab.ALL; } + @Override public int amountPerRowTwo() { return 8; } + @Override public java.awt.Color tickColourTwo() { return java.awt.Color.BLUE; } + @Override public java.awt.Color currentTickColourTwo() { return java.awt.Color.WHITE; } + @Override public int sizeOfTickShapesTwo() { return 32; } + @Override public int tickArcTwo() { return 10; } + @Override public boolean shouldShowTextTwo() { return true; } + @Override public int tickTextSizeTwo() { return 15; } + @Override public java.awt.Color tickTextColourTwo() { return java.awt.Color.WHITE; } + @Override public java.awt.Color currentTickTextColourTwo() { return java.awt.Color.BLUE; } + @Override public int horizontalSpacingTwo() { return 5; } + @Override public int verticalSpacingTwo() { return 5; } + @Override public boolean shouldShowTickShapeThree() { return true; } + @Override public com.visualticks.config.TickShape tickShapeThree() { return com.visualticks.config.TickShape.CIRCLE; } + @Override public com.visualticks.config.InterfaceTab exclusiveTabThree() { return com.visualticks.config.InterfaceTab.ALL; } + @Override public int amountPerRowThree() { return 8; } + @Override public java.awt.Color tickColourThree() { return java.awt.Color.BLUE; } + @Override public java.awt.Color currentTickColourThree() { return java.awt.Color.WHITE; } + @Override public int sizeOfTickShapesThree() { return 32; } + @Override public int tickArcThree() { return 10; } + @Override public boolean shouldShowTextThree() { return true; } + @Override public int tickTextSizeThree() { return 15; } + @Override public java.awt.Color tickTextColourThree() { return java.awt.Color.WHITE; } + @Override public java.awt.Color currentTickTextColourThree() { return java.awt.Color.BLUE; } + @Override public int horizontalSpacingThree() { return 5; } + @Override public int verticalSpacingThree() { return 5; } + @Override public boolean shouldShowTickShapeHotkeys() { return true; } + @Override public com.visualticks.config.TickShape tickShapeHotkeys() { return com.visualticks.config.TickShape.CIRCLE; } + @Override public com.visualticks.config.InterfaceTab exclusiveTabHotkeys() { return com.visualticks.config.InterfaceTab.ALL; } + @Override public int amountPerRowHotkeys() { return 8; } + @Override public java.awt.Color tickColourHotkeys() { return java.awt.Color.BLUE; } + @Override public java.awt.Color currentTickColourHotkeys() { return java.awt.Color.WHITE; } + @Override public int sizeOfTickShapesHotkeys() { return 32; } + @Override public int tickArcHotkeys() { return 10; } + @Override public boolean shouldShowTextHotkeys() { return true; } + @Override public int tickTextSizeHotkeys() { return 15; } + @Override public java.awt.Color tickTextColourHotkeys() { return java.awt.Color.WHITE; } + @Override public java.awt.Color currentTickTextColourHotkeys() { return java.awt.Color.BLUE; } + @Override public int horizontalSpacingHotkeys() { return 5; } + @Override public int verticalSpacingHotkeys() { return 5; } + } + + @Before + public void setUp() { + plugin = new VisualTicksPlugin(); + testConfig = new TestConfig(); + + // Use reflection to inject test configuration + try { + java.lang.reflect.Field configField = VisualTicksPlugin.class.getDeclaredField("config"); + configField.setAccessible(true); + configField.set(plugin, testConfig); + } catch (Exception e) { + throw new RuntimeException("Failed to inject test configuration", e); + } + } + + /** + * Test that overlay getCurrentTick() methods return updated values after tick manipulation. + * This verifies Requirements 1.5 and 2.5 - that visual overlays reflect tick changes. + */ + @Test + public void testOverlayReflectsTickManipulation() throws Exception { + // Create real overlay instances to test getCurrentTick() methods + VisualTicksOverlayOne overlayOne = new VisualTicksOverlayOne(plugin, testConfig, null); + VisualTicksOverlayTwo overlayTwo = new VisualTicksOverlayTwo(plugin, testConfig, null); + VisualTicksOverlayThree overlayThree = new VisualTicksOverlayThree(plugin, testConfig, null); + VisualTicksOverlayHotkeys overlayHotkeys = new VisualTicksOverlayHotkeys(plugin, testConfig, null); + + // Test initial state + assertEquals("Initial tickOne should be 0", 0, plugin.tickOne); + assertEquals("Initial tickTwo should be 0", 0, plugin.tickTwo); + assertEquals("Initial tickThree should be 0", 0, plugin.tickThree); + assertEquals("Initial tickHotkeys should be 0", 0, plugin.tickHotkeys); + + // Verify overlays return the same values as plugin fields + assertEquals("OverlayOne getCurrentTick should match plugin.tickOne", plugin.tickOne, overlayOne.getCurrentTick()); + assertEquals("OverlayTwo getCurrentTick should match plugin.tickTwo", plugin.tickTwo, overlayTwo.getCurrentTick()); + assertEquals("OverlayThree getCurrentTick should match plugin.tickThree", plugin.tickThree, overlayThree.getCurrentTick()); + assertEquals("OverlayHotkeys getCurrentTick should match plugin.tickHotkeys", plugin.tickHotkeys, overlayHotkeys.getCurrentTick()); + + // Use reflection to call private increment methods to simulate tick manipulation + java.lang.reflect.Method incrementMethod = VisualTicksPlugin.class.getDeclaredMethod("incrementTick", int.class); + incrementMethod.setAccessible(true); + + // Test increment operations + incrementMethod.invoke(plugin, 1); // Increment configuration One + assertEquals("After increment, tickOne should be 1", 1, plugin.tickOne); + assertEquals("OverlayOne should reflect incremented value", 1, overlayOne.getCurrentTick()); + + incrementMethod.invoke(plugin, 2); // Increment configuration Two + assertEquals("After increment, tickTwo should be 1", 1, plugin.tickTwo); + assertEquals("OverlayTwo should reflect incremented value", 1, overlayTwo.getCurrentTick()); + + incrementMethod.invoke(plugin, 3); // Increment configuration Three + assertEquals("After increment, tickThree should be 1", 1, plugin.tickThree); + assertEquals("OverlayThree should reflect incremented value", 1, overlayThree.getCurrentTick()); + + incrementMethod.invoke(plugin, 4); // Increment configuration Hotkeys + assertEquals("After increment, tickHotkeys should be 1", 1, plugin.tickHotkeys); + assertEquals("OverlayHotkeys should reflect incremented value", 1, overlayHotkeys.getCurrentTick()); + + // Test decrement operations + java.lang.reflect.Method decrementMethod = VisualTicksPlugin.class.getDeclaredMethod("decrementTick", int.class); + decrementMethod.setAccessible(true); + + // Decrement from 1 should give us 0 (normal decrement) + decrementMethod.invoke(plugin, 1); // Decrement configuration One + assertEquals("After decrement, tickOne should be 0", 0, plugin.tickOne); + assertEquals("OverlayOne should reflect decremented value", 0, overlayOne.getCurrentTick()); + + // Test decrement from 0 to see wrapping behavior + decrementMethod.invoke(plugin, 1); // Decrement configuration One from 0 + assertEquals("After decrement from 0, tickOne should wrap to 4", 4, plugin.tickOne); + assertEquals("OverlayOne should reflect wrapped value", 4, overlayOne.getCurrentTick()); + + // For configuration Two (numberOfTicksTwo = 3, valid values 0-2) + decrementMethod.invoke(plugin, 2); // Decrement configuration Two from 1 + assertEquals("After decrement, tickTwo should be 0", 0, plugin.tickTwo); + assertEquals("OverlayTwo should reflect decremented value", 0, overlayTwo.getCurrentTick()); + + // Test wrapping for configuration Two + decrementMethod.invoke(plugin, 2); // Decrement configuration Two from 0 + assertEquals("After decrement from 0, tickTwo should wrap to 2", 2, plugin.tickTwo); + assertEquals("OverlayTwo should reflect wrapped value", 2, overlayTwo.getCurrentTick()); + + // For configuration Three (numberOfTicksThree = 4, valid values 0-3) + decrementMethod.invoke(plugin, 3); // Decrement configuration Three from 1 + assertEquals("After decrement, tickThree should be 0", 0, plugin.tickThree); + assertEquals("OverlayThree should reflect decremented value", 0, overlayThree.getCurrentTick()); + + // Test wrapping for configuration Three + decrementMethod.invoke(plugin, 3); // Decrement configuration Three from 0 + assertEquals("After decrement from 0, tickThree should wrap to 3", 3, plugin.tickThree); + assertEquals("OverlayThree should reflect wrapped value", 3, overlayThree.getCurrentTick()); + + // For configuration Hotkeys (numberOfTicksHotkeys = 6, valid values 0-5) + decrementMethod.invoke(plugin, 4); // Decrement configuration Hotkeys from 1 + assertEquals("After decrement, tickHotkeys should be 0", 0, plugin.tickHotkeys); + assertEquals("OverlayHotkeys should reflect decremented value", 0, overlayHotkeys.getCurrentTick()); + + // Test wrapping for configuration Hotkeys + decrementMethod.invoke(plugin, 4); // Decrement configuration Hotkeys from 0 + assertEquals("After decrement from 0, tickHotkeys should wrap to 5", 5, plugin.tickHotkeys); + assertEquals("OverlayHotkeys should reflect wrapped value", 5, overlayHotkeys.getCurrentTick()); + } + + /** + * Test that overlays ignore tick manipulation when configurations are disabled. + * This verifies that disabled configurations maintain their state. + */ + @Test + public void testOverlayIgnoresDisabledConfigurations() throws Exception { + // Setup configuration with some configurations disabled + testConfig.setEnabledOne(false); // Disabled + testConfig.setEnabledTwo(true); // Enabled + + // Create real overlay instances + VisualTicksOverlayOne overlayOne = new VisualTicksOverlayOne(plugin, testConfig, null); + VisualTicksOverlayTwo overlayTwo = new VisualTicksOverlayTwo(plugin, testConfig, null); + + // Use reflection to call private increment methods + java.lang.reflect.Method incrementMethod = VisualTicksPlugin.class.getDeclaredMethod("incrementTick", int.class); + incrementMethod.setAccessible(true); + + // Test that disabled configuration is not affected + incrementMethod.invoke(plugin, 1); // Try to increment disabled configuration One + assertEquals("Disabled configuration should not change", 0, plugin.tickOne); + assertEquals("OverlayOne should reflect unchanged value", 0, overlayOne.getCurrentTick()); + + // Test that enabled configuration is affected + incrementMethod.invoke(plugin, 2); // Increment enabled configuration Two + assertEquals("Enabled configuration should change", 1, plugin.tickTwo); + assertEquals("OverlayTwo should reflect changed value", 1, overlayTwo.getCurrentTick()); + } + + /** + * Test that direct field changes are immediately reflected in overlay getCurrentTick() methods. + * This verifies that no additional update mechanism is needed beyond direct field access. + */ + @Test + public void testOverlayDirectFieldAccess() { + // Create overlay instances + VisualTicksOverlayOne overlayOne = new VisualTicksOverlayOne(plugin, testConfig, null); + VisualTicksOverlayTwo overlayTwo = new VisualTicksOverlayTwo(plugin, testConfig, null); + VisualTicksOverlayThree overlayThree = new VisualTicksOverlayThree(plugin, testConfig, null); + VisualTicksOverlayHotkeys overlayHotkeys = new VisualTicksOverlayHotkeys(plugin, testConfig, null); + + // Directly modify plugin tick fields + plugin.tickOne = 3; + plugin.tickTwo = 1; + plugin.tickThree = 2; + plugin.tickHotkeys = 4; + + // Verify overlays immediately reflect the changes + assertEquals("OverlayOne should immediately reflect field change", 3, overlayOne.getCurrentTick()); + assertEquals("OverlayTwo should immediately reflect field change", 1, overlayTwo.getCurrentTick()); + assertEquals("OverlayThree should immediately reflect field change", 2, overlayThree.getCurrentTick()); + assertEquals("OverlayHotkeys should immediately reflect field change", 4, overlayHotkeys.getCurrentTick()); + + // Change values again + plugin.tickOne = 0; + plugin.tickTwo = 2; + plugin.tickThree = 1; + plugin.tickHotkeys = 5; + + // Verify overlays still reflect the changes + assertEquals("OverlayOne should reflect second field change", 0, overlayOne.getCurrentTick()); + assertEquals("OverlayTwo should reflect second field change", 2, overlayTwo.getCurrentTick()); + assertEquals("OverlayThree should reflect second field change", 1, overlayThree.getCurrentTick()); + assertEquals("OverlayHotkeys should reflect second field change", 5, overlayHotkeys.getCurrentTick()); + } + public static void main(String[] args) throws Exception { ExternalPluginManager.loadBuiltin(VisualTicksPlugin.class); From a6e86a1efa0c7af142baa05cef8da8e359ef3277 Mon Sep 17 00:00:00 2001 From: mcscoobe Date: Mon, 9 Mar 2026 23:56:40 -0400 Subject: [PATCH 2/2] Revert "Tick change shortcut feature" This reverts commit 802e7f628d8890edc4b80d56af62f2b1a59bfaf5. --- .../visualticks/BaseVisualTicksOverlay.java | 7 +- .../com/visualticks/VisualTicksConfig.java | 217 -------------- .../VisualTicksOverlayHotkeys.java | 98 ------- .../com/visualticks/VisualTicksPlugin.java | 225 +-------------- .../visualticks/VisualTicksPluginTest.java | 266 ------------------ 5 files changed, 2 insertions(+), 811 deletions(-) delete mode 100644 src/main/java/com/visualticks/VisualTicksOverlayHotkeys.java diff --git a/src/main/java/com/visualticks/BaseVisualTicksOverlay.java b/src/main/java/com/visualticks/BaseVisualTicksOverlay.java index c2129a5..5d7a419 100644 --- a/src/main/java/com/visualticks/BaseVisualTicksOverlay.java +++ b/src/main/java/com/visualticks/BaseVisualTicksOverlay.java @@ -111,18 +111,13 @@ public Dimension render(Graphics2D graphics) } if(getExclusiveTab().getIndex() != -1 && client.getVarcIntValue(VarClientInt.INVENTORY_TAB) != getExclusiveTab().getIndex()) return null; - if(ticks.size() < getNumberOfTicks()) return null; + if(ticks.size() < getNumberOfTicks() - 1) return null; Font originalFont = graphics.getFont(); graphics.setFont(graphics.getFont().deriveFont((float) getTickTextSize())); for (int i = 0; i < getNumberOfTicks(); i++) { - // Additional safety check to prevent IndexOutOfBoundsException - if (i >= ticks.size()) { - break; - } - Tick tick = ticks.get(i); if (shouldShowTickShape()) { graphics.setColor(i == getCurrentTick() ? getCurrentTickColour() : getTickColour()); diff --git a/src/main/java/com/visualticks/VisualTicksConfig.java b/src/main/java/com/visualticks/VisualTicksConfig.java index 1d00d9f..d790129 100644 --- a/src/main/java/com/visualticks/VisualTicksConfig.java +++ b/src/main/java/com/visualticks/VisualTicksConfig.java @@ -613,221 +613,4 @@ default int verticalSpacingThree() { return 5; } //endregion - - //region Tick settings - Hotkeys - @ConfigSection( - name = "Tick settings - Hotkeys", - description = "Settings for the hotkey-controlled tick configuration", - position = 4 - ) - String tickSettingsHotkeys = "tickSettingsHotkeys"; - - @ConfigItem( - keyName = "isEnabledHotkeys", - name = "Enabled", - description = "Enable the hotkey-controlled tick configuration", - section = tickSettingsHotkeys, - position = 0 - ) - default boolean isEnabledHotkeys() { - return false; - } - - @ConfigItem( - keyName = "shouldShowTickShapeHotkeys", - name = "Show Tick Shape", - description = "Show the shape of the hotkey-controlled ticks", - section = tickSettingsHotkeys, - position = 1 - ) - default boolean shouldShowTickShapeHotkeys() { - return true; - } - - @ConfigItem( - keyName = "tickShapeHotkeys", - name = "Tick shape", - description = "The shape of the ticks", - section = tickSettingsHotkeys, - position = 2 - ) - default TickShape tickShapeHotkeys() { - return TickShape.CIRCLE; - } - - @ConfigItem( - keyName = "exclusiveTabHotkeys", - name = "Only show if on tab", - description = "Show the ticks only when the selected tab is active", - section = tickSettingsHotkeys, - position = 3 - ) - default InterfaceTab exclusiveTabHotkeys() { - return InterfaceTab.ALL; - } - - @ConfigItem( - keyName = "numberOfTicksHotkeys", - name = "Number of ticks", - description = "Number of tick circles to display", - section = tickSettingsHotkeys, - position = 4 - ) - @Range(min = 2, max = 30) - default int numberOfTicksHotkeys() { - return 2; - } - - @ConfigItem( - keyName = "amountPerRowHotkeys", - name = "Amount per row", - description = "How many ticks to display per row", - section = tickSettingsHotkeys, - position = 5 - ) - @Range(min = 1) - default int amountPerRowHotkeys() { - return 8; - } - - @ConfigItem( - keyName = "tickColourHotkeys", - name = "Tick colour", - description = "The colour of the ticks", - section = tickSettingsHotkeys, - position = 6 - ) - @Alpha - default Color tickColourHotkeys() { - return new Color(41, 128, 185); - } - - @ConfigItem( - keyName = "currentTickColourHotkeys", - name = "Current tick colour", - description = "The colour of the current tick", - section = tickSettingsHotkeys, - position = 7 - ) - @Alpha - default Color currentTickColourHotkeys() { - return new Color(236, 240, 241); - } - - @ConfigItem( - keyName = "sizeOfTickShapesHotkeys", - name = "Size of ticks", - description = "How many pixels to make the tick shapes", - section = tickSettingsHotkeys, - position = 8 - ) - default int sizeOfTickShapesHotkeys() { - return 32; - } - - @ConfigItem( - keyName = "tickArcHotkeys", - name = "Tick arc(rounded square)", - description = "The arc of the corners of the rounded square tick shape", - section = tickSettingsHotkeys, - position = 9 - ) - @Range(max = 100) - default int tickArcHotkeys() { - return 10; - } - - @ConfigItem( - keyName = "shouldShowTextHotkeys", - name = "Show text", - description = "Show the text of the current tick", - section = tickSettingsHotkeys, - position = 10 - ) - default boolean shouldShowTextHotkeys() { - return true; - } - - @ConfigItem( - keyName = "tickTextSizeHotkeys", - name = "Tick text size", - description = "The size of the text on the ticks", - section = tickSettingsHotkeys, - position = 11 - ) - @Range(min = 1, max = 100) - default int tickTextSizeHotkeys() { - return 15; - } - - @ConfigItem( - keyName = "tickTextColourHotkeys", - name = "Tick text colour", - description = "The colour of the text non-current ticks", - section = tickSettingsHotkeys, - position = 12 - ) - @Alpha - default Color tickTextColourHotkeys() { - return new Color(236, 240, 241); - } - - @ConfigItem( - keyName = "currentTickTextColourHotkeys", - name = "Current tick text colour", - description = "The colour of the text on the current tick", - section = tickSettingsHotkeys, - position = 13 - ) - @Alpha - default Color currentTickTextColourHotkeys() { - return new Color(41, 128, 185); - } - - @ConfigItem( - keyName = "horizontalSpacingHotkeys", - name = "Horizontal spacing", - description = "The amount of space between ticks on the x-axis", - section = tickSettingsHotkeys, - position = 14 - ) - @Range(min = -50) - default int horizontalSpacingHotkeys() { - return 5; - } - - @ConfigItem( - keyName = "verticalSpacingHotkeys", - name = "Vertical spacing", - description = "The amount of space between ticks on the y-axis", - section = tickSettingsHotkeys, - position = 15 - ) - @Range(min = -50) - default int verticalSpacingHotkeys() { - return 5; - } - - @ConfigItem( - position = 16, - keyName = "tickIncrementHotkey", - name = "Tick Increment Hotkey", - description = "Hotkey to increment number of ticks", - section = tickSettingsHotkeys - ) - default Keybind tickIncrementHotkey() { - return new Keybind(KeyEvent.VK_EQUALS, KeyEvent.VK_UNDEFINED); - } - - @ConfigItem( - position = 17, - keyName = "tickDecrementHotkey", - name = "Tick Decrement Hotkey", - description = "Hotkey to decrement number of ticks", - section = tickSettingsHotkeys - ) - default Keybind tickDecrementHotkey() { - return new Keybind(KeyEvent.VK_MINUS, KeyEvent.VK_UNDEFINED); - } - //endregion } diff --git a/src/main/java/com/visualticks/VisualTicksOverlayHotkeys.java b/src/main/java/com/visualticks/VisualTicksOverlayHotkeys.java deleted file mode 100644 index 74434bc..0000000 --- a/src/main/java/com/visualticks/VisualTicksOverlayHotkeys.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.visualticks; - -import com.visualticks.config.InterfaceTab; -import com.visualticks.config.TickShape; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; -import javax.inject.Inject; -import java.awt.*; - -@Slf4j -public class VisualTicksOverlayHotkeys extends BaseVisualTicksOverlay -{ - @Inject - public VisualTicksOverlayHotkeys(VisualTicksPlugin plugin, VisualTicksConfig config, Client client) - { - super(plugin, config, client); - } - - @Override - protected boolean shouldShowText() { - return config.shouldShowTextHotkeys(); - } - - @Override - protected boolean shouldShowTickShape() { - return config.shouldShowTickShapeHotkeys(); - } - - @Override - protected int getTickTextSize() { - return config.tickTextSizeHotkeys(); - } - - @Override - protected int getNumberOfTicks() { - return config.numberOfTicksHotkeys(); - } - - @Override - protected Color getTickColour() { - return config.tickColourHotkeys(); - } - - @Override - protected Color getCurrentTickColour() { - return config.currentTickColourHotkeys(); - } - - @Override - protected int getAmountPerRow() { - return config.amountPerRowHotkeys(); - } - - @Override - protected int getSizeOfTickShapes() { - return config.sizeOfTickShapesHotkeys(); - } - - @Override - protected int getHorizontalSpacing() { - return config.horizontalSpacingHotkeys(); - } - - @Override - protected int getVerticalSpacing() { - return config.verticalSpacingHotkeys(); - } - - @Override - protected int getCurrentTick() { - return plugin.tickHotkeys; - } - - @Override - protected InterfaceTab getExclusiveTab() { - return config.exclusiveTabHotkeys(); - } - - @Override - protected Color getTickTextColour() { - return config.tickTextColourHotkeys(); - } - - @Override - protected Color getCurrentTickTextColour() { - return config.currentTickTextColourHotkeys(); - } - - @Override - protected TickShape getTickShape() { - return config.tickShapeHotkeys(); - } - - @Override - protected int getTickArc() { - return config.tickArcHotkeys(); - } -} \ No newline at end of file diff --git a/src/main/java/com/visualticks/VisualTicksPlugin.java b/src/main/java/com/visualticks/VisualTicksPlugin.java index bcb7310..6aacbc9 100644 --- a/src/main/java/com/visualticks/VisualTicksPlugin.java +++ b/src/main/java/com/visualticks/VisualTicksPlugin.java @@ -31,15 +31,12 @@ public class VisualTicksPlugin extends Plugin implements KeyListener { @Inject private VisualTicksOverlayThree overlayThree; @Inject - private VisualTicksOverlayHotkeys overlayHotkeys; - @Inject private KeyManager keyManager; @Inject private ConfigManager configManager; public int tickOne = 0; public int tickTwo = 0; public int tickThree = 0; - public int tickHotkeys = 0; @Override protected void startUp() throws Exception { @@ -53,13 +50,12 @@ protected void shutDown() throws Exception { overlayManager.remove(overlayOne); overlayManager.remove(overlayTwo); overlayManager.remove(overlayThree); - overlayManager.remove(overlayHotkeys); keyManager.unregisterKeyListener(this); } @Subscribe private void onGameTick(GameTick gameTick) { - if (!config.isEnabledOne() && !config.isEnabledTwo() && !config.isEnabledThree() && !config.isEnabledHotkeys()) { + if (!config.isEnabledOne() && !config.isEnabledTwo() && !config.isEnabledThree()) { return; } @@ -74,10 +70,6 @@ private void onGameTick(GameTick gameTick) { if(config.isEnabledThree()) { tickThree = (tickThree + 1) % config.numberOfTicksThree(); } - - if(config.isEnabledHotkeys()) { - tickHotkeys = (tickHotkeys + 1) % config.numberOfTicksHotkeys(); - } } @Subscribe @@ -86,19 +78,6 @@ private void onConfigChanged(ConfigChanged event) { return; } - // Handle hotkey configuration changes - String key = event.getKey(); - if (isHotkeyConfigurationKey(key)) { - // Re-register hotkeys with KeyManager when hotkey configs change - keyManager.unregisterKeyListener(this); - keyManager.registerKeyListener(this); - } - - // Handle numberOfTicks changes with proper tick value adjustment - if (isNumberOfTicksConfigurationKey(key)) { - adjustTickValuesForConfigurationChange(key); - } - updateOverlays(); } @@ -106,7 +85,6 @@ private void updateOverlays() { overlayManager.remove(overlayOne); overlayManager.remove(overlayTwo); overlayManager.remove(overlayThree); - overlayManager.remove(overlayHotkeys); if (config.isEnabledOne()) { overlayManager.add(overlayOne); @@ -117,14 +95,10 @@ private void updateOverlays() { if (config.isEnabledThree()) { overlayManager.add(overlayThree); } - if (config.isEnabledHotkeys()) { - overlayManager.add(overlayHotkeys); - } overlayOne.onConfigChanged(); overlayTwo.onConfigChanged(); overlayThree.onConfigChanged(); - overlayHotkeys.onConfigChanged(); } @Subscribe @@ -144,71 +118,8 @@ public void keyTyped(KeyEvent e) { @Override public void keyPressed(KeyEvent e) { - // Existing tick reset functionality if (config.tickResetHotkey().matches(e)) { resetTicks(); - return; - } - - // Handle increment/decrement hotkeys for hotkey configuration only - handleIncrementDecrementKeys(e); - } - - /** - * Handles increment and decrement hotkey presses for the hotkey configuration. - * Increments/decrements the numberOfTicksHotkeys configuration value. - * - * @param e The KeyEvent to process - */ - private void handleIncrementDecrementKeys(KeyEvent e) { - // Hotkey configuration increment/decrement for numberOfTicks - if (config.tickIncrementHotkey().matches(e)) { - incrementNumberOfTicks(); - return; - } - if (config.tickDecrementHotkey().matches(e)) { - decrementNumberOfTicks(); - return; - } - } - - /** - * Increments the numberOfTicksHotkeys configuration value. - * If the Hotkeys configuration is disabled, enables it and sets to 2 ticks. - * Otherwise increments up to maximum of 30. - */ - private void incrementNumberOfTicks() { - if (!config.isEnabledHotkeys()) { - // If disabled, enable it and set to minimum (2 ticks) - configManager.setConfiguration(VisualTicksConfig.GROUP_NAME, "isEnabledHotkeys", true); - configManager.setConfiguration(VisualTicksConfig.GROUP_NAME, "numberOfTicksHotkeys", 2); - } else { - // If enabled, increment up to maximum - int currentValue = config.numberOfTicksHotkeys(); - if (currentValue < 30) { // Max value from @Range annotation - configManager.setConfiguration(VisualTicksConfig.GROUP_NAME, "numberOfTicksHotkeys", currentValue + 1); - } - } - } - - /** - * Decrements the numberOfTicksHotkeys configuration value. - * If already at minimum (2 ticks), disables the Hotkeys configuration. - * Otherwise decrements down to minimum of 2. - */ - private void decrementNumberOfTicks() { - if (!config.isEnabledHotkeys()) { - // If already disabled, do nothing - return; - } - - int currentValue = config.numberOfTicksHotkeys(); - if (currentValue <= 2) { - // If at minimum, disable the configuration - configManager.setConfiguration(VisualTicksConfig.GROUP_NAME, "isEnabledHotkeys", false); - } else { - // Otherwise decrement normally - configManager.setConfiguration(VisualTicksConfig.GROUP_NAME, "numberOfTicksHotkeys", currentValue - 1); } } @@ -243,139 +154,5 @@ private void resetTicks() { tickOne = 0; tickTwo = 0; tickThree = 0; - tickHotkeys = 0; - } - - /** - * Increments the tick count for the specified configuration. - * Uses modulo arithmetic to wrap around when reaching the maximum tick count. - * Only operates on enabled configurations. - * - * @param configurationNumber The configuration to increment (1, 2, 3, or 4) - */ - private void incrementTick(int configurationNumber) { - if (!isConfigurationEnabled(configurationNumber)) { - return; - } - - switch (configurationNumber) { - case 1: - tickOne = (tickOne + 1) % config.numberOfTicksOne(); - break; - case 2: - tickTwo = (tickTwo + 1) % config.numberOfTicksTwo(); - break; - case 3: - tickThree = (tickThree + 1) % config.numberOfTicksThree(); - break; - case 4: - tickHotkeys = (tickHotkeys + 1) % config.numberOfTicksHotkeys(); - break; - } - } - - /** - * Decrements the tick count for the specified configuration. - * Uses modulo arithmetic to wrap around when going below zero. - * Only operates on enabled configurations. - * - * @param configurationNumber The configuration to decrement (1, 2, 3, or 4) - */ - private void decrementTick(int configurationNumber) { - if (!isConfigurationEnabled(configurationNumber)) { - return; - } - - switch (configurationNumber) { - case 1: - tickOne = (tickOne - 1 + config.numberOfTicksOne()) % config.numberOfTicksOne(); - break; - case 2: - tickTwo = (tickTwo - 1 + config.numberOfTicksTwo()) % config.numberOfTicksTwo(); - break; - case 3: - tickThree = (tickThree - 1 + config.numberOfTicksThree()) % config.numberOfTicksThree(); - break; - case 4: - tickHotkeys = (tickHotkeys - 1 + config.numberOfTicksHotkeys()) % config.numberOfTicksHotkeys(); - break; - } - } - - /** - * Helper method to check if a configuration is enabled before manipulation. - * - * @param configurationNumber The configuration to check (1, 2, 3, or 4) - * @return true if the configuration is enabled, false otherwise - */ - private boolean isConfigurationEnabled(int configurationNumber) { - switch (configurationNumber) { - case 1: - return config.isEnabledOne(); - case 2: - return config.isEnabledTwo(); - case 3: - return config.isEnabledThree(); - case 4: - return config.isEnabledHotkeys(); - default: - return false; - } - } - - /** - * Checks if the given configuration key is a hotkey configuration. - * - * @param key The configuration key to check - * @return true if the key is a hotkey configuration, false otherwise - */ - private boolean isHotkeyConfigurationKey(String key) { - return "tickResetHotkey".equals(key) || - "tickIncrementHotkey".equals(key) || - "tickDecrementHotkey".equals(key); - } - - /** - * Checks if the given configuration key is a numberOfTicks configuration. - * - * @param key The configuration key to check - * @return true if the key is a numberOfTicks configuration, false otherwise - */ - private boolean isNumberOfTicksConfigurationKey(String key) { - return "numberOfTicksOne".equals(key) || - "numberOfTicksTwo".equals(key) || - "numberOfTicksThree".equals(key) || - "numberOfTicksHotkeys".equals(key); - } - - /** - * Adjusts tick values when numberOfTicks configuration changes. - * Uses modulo operation to ensure tick values remain within valid range. - * - * @param key The configuration key that changed - */ - private void adjustTickValuesForConfigurationChange(String key) { - switch (key) { - case "numberOfTicksOne": - if (config.numberOfTicksOne() > 0) { - tickOne = tickOne % config.numberOfTicksOne(); - } - break; - case "numberOfTicksTwo": - if (config.numberOfTicksTwo() > 0) { - tickTwo = tickTwo % config.numberOfTicksTwo(); - } - break; - case "numberOfTicksThree": - if (config.numberOfTicksThree() > 0) { - tickThree = tickThree % config.numberOfTicksThree(); - } - break; - case "numberOfTicksHotkeys": - if (config.numberOfTicksHotkeys() > 0) { - tickHotkeys = tickHotkeys % config.numberOfTicksHotkeys(); - } - break; - } } } diff --git a/src/test/java/com/visualticks/VisualTicksPluginTest.java b/src/test/java/com/visualticks/VisualTicksPluginTest.java index 24bc91d..a77bd18 100644 --- a/src/test/java/com/visualticks/VisualTicksPluginTest.java +++ b/src/test/java/com/visualticks/VisualTicksPluginTest.java @@ -2,275 +2,9 @@ import net.runelite.client.RuneLite; import net.runelite.client.externalplugins.ExternalPluginManager; -import org.junit.Test; -import org.junit.Before; -import net.runelite.api.Client; - -import static org.junit.Assert.*; public class VisualTicksPluginTest { - private VisualTicksPlugin plugin; - private TestConfig testConfig; - - /** - * Simple test configuration implementation for testing overlay behavior. - */ - private static class TestConfig implements VisualTicksConfig { - private int numberOfTicksOne = 5; - private int numberOfTicksTwo = 3; - private int numberOfTicksThree = 4; - private int numberOfTicksHotkeys = 6; - private boolean enabledOne = true; - private boolean enabledTwo = true; - private boolean enabledThree = true; - private boolean enabledHotkeys = true; - - @Override public int numberOfTicksOne() { return numberOfTicksOne; } - @Override public int numberOfTicksTwo() { return numberOfTicksTwo; } - @Override public int numberOfTicksThree() { return numberOfTicksThree; } - @Override public int numberOfTicksHotkeys() { return numberOfTicksHotkeys; } - @Override public boolean isEnabledOne() { return enabledOne; } - @Override public boolean isEnabledTwo() { return enabledTwo; } - @Override public boolean isEnabledThree() { return enabledThree; } - @Override public boolean isEnabledHotkeys() { return enabledHotkeys; } - - public void setEnabledOne(boolean enabled) { this.enabledOne = enabled; } - public void setEnabledTwo(boolean enabled) { this.enabledTwo = enabled; } - - // Default implementations for other required methods - @Override public net.runelite.client.config.Keybind tickResetHotkey() { return null; } - @Override public net.runelite.client.config.Keybind tickIncrementHotkey() { return null; } - @Override public net.runelite.client.config.Keybind tickDecrementHotkey() { return null; } - @Override public boolean shouldShowTickShapeOne() { return true; } - @Override public com.visualticks.config.TickShape tickShapeOne() { return com.visualticks.config.TickShape.CIRCLE; } - @Override public com.visualticks.config.InterfaceTab exclusiveTabOne() { return com.visualticks.config.InterfaceTab.ALL; } - @Override public int amountPerRowOne() { return 8; } - @Override public java.awt.Color tickColourOne() { return java.awt.Color.BLUE; } - @Override public java.awt.Color currentTickColourOne() { return java.awt.Color.WHITE; } - @Override public int sizeOfTickShapesOne() { return 32; } - @Override public int tickArcOne() { return 10; } - @Override public boolean shouldShowTextOne() { return true; } - @Override public int tickTextSizeOne() { return 15; } - @Override public java.awt.Color tickTextColourOne() { return java.awt.Color.WHITE; } - @Override public java.awt.Color currentTickTextColourOne() { return java.awt.Color.BLUE; } - @Override public int horizontalSpacingOne() { return 5; } - @Override public int verticalSpacingOne() { return 5; } - @Override public boolean shouldShowTickShapeTwo() { return true; } - @Override public com.visualticks.config.TickShape tickShapeTwo() { return com.visualticks.config.TickShape.CIRCLE; } - @Override public com.visualticks.config.InterfaceTab exclusiveTabTwo() { return com.visualticks.config.InterfaceTab.ALL; } - @Override public int amountPerRowTwo() { return 8; } - @Override public java.awt.Color tickColourTwo() { return java.awt.Color.BLUE; } - @Override public java.awt.Color currentTickColourTwo() { return java.awt.Color.WHITE; } - @Override public int sizeOfTickShapesTwo() { return 32; } - @Override public int tickArcTwo() { return 10; } - @Override public boolean shouldShowTextTwo() { return true; } - @Override public int tickTextSizeTwo() { return 15; } - @Override public java.awt.Color tickTextColourTwo() { return java.awt.Color.WHITE; } - @Override public java.awt.Color currentTickTextColourTwo() { return java.awt.Color.BLUE; } - @Override public int horizontalSpacingTwo() { return 5; } - @Override public int verticalSpacingTwo() { return 5; } - @Override public boolean shouldShowTickShapeThree() { return true; } - @Override public com.visualticks.config.TickShape tickShapeThree() { return com.visualticks.config.TickShape.CIRCLE; } - @Override public com.visualticks.config.InterfaceTab exclusiveTabThree() { return com.visualticks.config.InterfaceTab.ALL; } - @Override public int amountPerRowThree() { return 8; } - @Override public java.awt.Color tickColourThree() { return java.awt.Color.BLUE; } - @Override public java.awt.Color currentTickColourThree() { return java.awt.Color.WHITE; } - @Override public int sizeOfTickShapesThree() { return 32; } - @Override public int tickArcThree() { return 10; } - @Override public boolean shouldShowTextThree() { return true; } - @Override public int tickTextSizeThree() { return 15; } - @Override public java.awt.Color tickTextColourThree() { return java.awt.Color.WHITE; } - @Override public java.awt.Color currentTickTextColourThree() { return java.awt.Color.BLUE; } - @Override public int horizontalSpacingThree() { return 5; } - @Override public int verticalSpacingThree() { return 5; } - @Override public boolean shouldShowTickShapeHotkeys() { return true; } - @Override public com.visualticks.config.TickShape tickShapeHotkeys() { return com.visualticks.config.TickShape.CIRCLE; } - @Override public com.visualticks.config.InterfaceTab exclusiveTabHotkeys() { return com.visualticks.config.InterfaceTab.ALL; } - @Override public int amountPerRowHotkeys() { return 8; } - @Override public java.awt.Color tickColourHotkeys() { return java.awt.Color.BLUE; } - @Override public java.awt.Color currentTickColourHotkeys() { return java.awt.Color.WHITE; } - @Override public int sizeOfTickShapesHotkeys() { return 32; } - @Override public int tickArcHotkeys() { return 10; } - @Override public boolean shouldShowTextHotkeys() { return true; } - @Override public int tickTextSizeHotkeys() { return 15; } - @Override public java.awt.Color tickTextColourHotkeys() { return java.awt.Color.WHITE; } - @Override public java.awt.Color currentTickTextColourHotkeys() { return java.awt.Color.BLUE; } - @Override public int horizontalSpacingHotkeys() { return 5; } - @Override public int verticalSpacingHotkeys() { return 5; } - } - - @Before - public void setUp() { - plugin = new VisualTicksPlugin(); - testConfig = new TestConfig(); - - // Use reflection to inject test configuration - try { - java.lang.reflect.Field configField = VisualTicksPlugin.class.getDeclaredField("config"); - configField.setAccessible(true); - configField.set(plugin, testConfig); - } catch (Exception e) { - throw new RuntimeException("Failed to inject test configuration", e); - } - } - - /** - * Test that overlay getCurrentTick() methods return updated values after tick manipulation. - * This verifies Requirements 1.5 and 2.5 - that visual overlays reflect tick changes. - */ - @Test - public void testOverlayReflectsTickManipulation() throws Exception { - // Create real overlay instances to test getCurrentTick() methods - VisualTicksOverlayOne overlayOne = new VisualTicksOverlayOne(plugin, testConfig, null); - VisualTicksOverlayTwo overlayTwo = new VisualTicksOverlayTwo(plugin, testConfig, null); - VisualTicksOverlayThree overlayThree = new VisualTicksOverlayThree(plugin, testConfig, null); - VisualTicksOverlayHotkeys overlayHotkeys = new VisualTicksOverlayHotkeys(plugin, testConfig, null); - - // Test initial state - assertEquals("Initial tickOne should be 0", 0, plugin.tickOne); - assertEquals("Initial tickTwo should be 0", 0, plugin.tickTwo); - assertEquals("Initial tickThree should be 0", 0, plugin.tickThree); - assertEquals("Initial tickHotkeys should be 0", 0, plugin.tickHotkeys); - - // Verify overlays return the same values as plugin fields - assertEquals("OverlayOne getCurrentTick should match plugin.tickOne", plugin.tickOne, overlayOne.getCurrentTick()); - assertEquals("OverlayTwo getCurrentTick should match plugin.tickTwo", plugin.tickTwo, overlayTwo.getCurrentTick()); - assertEquals("OverlayThree getCurrentTick should match plugin.tickThree", plugin.tickThree, overlayThree.getCurrentTick()); - assertEquals("OverlayHotkeys getCurrentTick should match plugin.tickHotkeys", plugin.tickHotkeys, overlayHotkeys.getCurrentTick()); - - // Use reflection to call private increment methods to simulate tick manipulation - java.lang.reflect.Method incrementMethod = VisualTicksPlugin.class.getDeclaredMethod("incrementTick", int.class); - incrementMethod.setAccessible(true); - - // Test increment operations - incrementMethod.invoke(plugin, 1); // Increment configuration One - assertEquals("After increment, tickOne should be 1", 1, plugin.tickOne); - assertEquals("OverlayOne should reflect incremented value", 1, overlayOne.getCurrentTick()); - - incrementMethod.invoke(plugin, 2); // Increment configuration Two - assertEquals("After increment, tickTwo should be 1", 1, plugin.tickTwo); - assertEquals("OverlayTwo should reflect incremented value", 1, overlayTwo.getCurrentTick()); - - incrementMethod.invoke(plugin, 3); // Increment configuration Three - assertEquals("After increment, tickThree should be 1", 1, plugin.tickThree); - assertEquals("OverlayThree should reflect incremented value", 1, overlayThree.getCurrentTick()); - - incrementMethod.invoke(plugin, 4); // Increment configuration Hotkeys - assertEquals("After increment, tickHotkeys should be 1", 1, plugin.tickHotkeys); - assertEquals("OverlayHotkeys should reflect incremented value", 1, overlayHotkeys.getCurrentTick()); - - // Test decrement operations - java.lang.reflect.Method decrementMethod = VisualTicksPlugin.class.getDeclaredMethod("decrementTick", int.class); - decrementMethod.setAccessible(true); - - // Decrement from 1 should give us 0 (normal decrement) - decrementMethod.invoke(plugin, 1); // Decrement configuration One - assertEquals("After decrement, tickOne should be 0", 0, plugin.tickOne); - assertEquals("OverlayOne should reflect decremented value", 0, overlayOne.getCurrentTick()); - - // Test decrement from 0 to see wrapping behavior - decrementMethod.invoke(plugin, 1); // Decrement configuration One from 0 - assertEquals("After decrement from 0, tickOne should wrap to 4", 4, plugin.tickOne); - assertEquals("OverlayOne should reflect wrapped value", 4, overlayOne.getCurrentTick()); - - // For configuration Two (numberOfTicksTwo = 3, valid values 0-2) - decrementMethod.invoke(plugin, 2); // Decrement configuration Two from 1 - assertEquals("After decrement, tickTwo should be 0", 0, plugin.tickTwo); - assertEquals("OverlayTwo should reflect decremented value", 0, overlayTwo.getCurrentTick()); - - // Test wrapping for configuration Two - decrementMethod.invoke(plugin, 2); // Decrement configuration Two from 0 - assertEquals("After decrement from 0, tickTwo should wrap to 2", 2, plugin.tickTwo); - assertEquals("OverlayTwo should reflect wrapped value", 2, overlayTwo.getCurrentTick()); - - // For configuration Three (numberOfTicksThree = 4, valid values 0-3) - decrementMethod.invoke(plugin, 3); // Decrement configuration Three from 1 - assertEquals("After decrement, tickThree should be 0", 0, plugin.tickThree); - assertEquals("OverlayThree should reflect decremented value", 0, overlayThree.getCurrentTick()); - - // Test wrapping for configuration Three - decrementMethod.invoke(plugin, 3); // Decrement configuration Three from 0 - assertEquals("After decrement from 0, tickThree should wrap to 3", 3, plugin.tickThree); - assertEquals("OverlayThree should reflect wrapped value", 3, overlayThree.getCurrentTick()); - - // For configuration Hotkeys (numberOfTicksHotkeys = 6, valid values 0-5) - decrementMethod.invoke(plugin, 4); // Decrement configuration Hotkeys from 1 - assertEquals("After decrement, tickHotkeys should be 0", 0, plugin.tickHotkeys); - assertEquals("OverlayHotkeys should reflect decremented value", 0, overlayHotkeys.getCurrentTick()); - - // Test wrapping for configuration Hotkeys - decrementMethod.invoke(plugin, 4); // Decrement configuration Hotkeys from 0 - assertEquals("After decrement from 0, tickHotkeys should wrap to 5", 5, plugin.tickHotkeys); - assertEquals("OverlayHotkeys should reflect wrapped value", 5, overlayHotkeys.getCurrentTick()); - } - - /** - * Test that overlays ignore tick manipulation when configurations are disabled. - * This verifies that disabled configurations maintain their state. - */ - @Test - public void testOverlayIgnoresDisabledConfigurations() throws Exception { - // Setup configuration with some configurations disabled - testConfig.setEnabledOne(false); // Disabled - testConfig.setEnabledTwo(true); // Enabled - - // Create real overlay instances - VisualTicksOverlayOne overlayOne = new VisualTicksOverlayOne(plugin, testConfig, null); - VisualTicksOverlayTwo overlayTwo = new VisualTicksOverlayTwo(plugin, testConfig, null); - - // Use reflection to call private increment methods - java.lang.reflect.Method incrementMethod = VisualTicksPlugin.class.getDeclaredMethod("incrementTick", int.class); - incrementMethod.setAccessible(true); - - // Test that disabled configuration is not affected - incrementMethod.invoke(plugin, 1); // Try to increment disabled configuration One - assertEquals("Disabled configuration should not change", 0, plugin.tickOne); - assertEquals("OverlayOne should reflect unchanged value", 0, overlayOne.getCurrentTick()); - - // Test that enabled configuration is affected - incrementMethod.invoke(plugin, 2); // Increment enabled configuration Two - assertEquals("Enabled configuration should change", 1, plugin.tickTwo); - assertEquals("OverlayTwo should reflect changed value", 1, overlayTwo.getCurrentTick()); - } - - /** - * Test that direct field changes are immediately reflected in overlay getCurrentTick() methods. - * This verifies that no additional update mechanism is needed beyond direct field access. - */ - @Test - public void testOverlayDirectFieldAccess() { - // Create overlay instances - VisualTicksOverlayOne overlayOne = new VisualTicksOverlayOne(plugin, testConfig, null); - VisualTicksOverlayTwo overlayTwo = new VisualTicksOverlayTwo(plugin, testConfig, null); - VisualTicksOverlayThree overlayThree = new VisualTicksOverlayThree(plugin, testConfig, null); - VisualTicksOverlayHotkeys overlayHotkeys = new VisualTicksOverlayHotkeys(plugin, testConfig, null); - - // Directly modify plugin tick fields - plugin.tickOne = 3; - plugin.tickTwo = 1; - plugin.tickThree = 2; - plugin.tickHotkeys = 4; - - // Verify overlays immediately reflect the changes - assertEquals("OverlayOne should immediately reflect field change", 3, overlayOne.getCurrentTick()); - assertEquals("OverlayTwo should immediately reflect field change", 1, overlayTwo.getCurrentTick()); - assertEquals("OverlayThree should immediately reflect field change", 2, overlayThree.getCurrentTick()); - assertEquals("OverlayHotkeys should immediately reflect field change", 4, overlayHotkeys.getCurrentTick()); - - // Change values again - plugin.tickOne = 0; - plugin.tickTwo = 2; - plugin.tickThree = 1; - plugin.tickHotkeys = 5; - - // Verify overlays still reflect the changes - assertEquals("OverlayOne should reflect second field change", 0, overlayOne.getCurrentTick()); - assertEquals("OverlayTwo should reflect second field change", 2, overlayTwo.getCurrentTick()); - assertEquals("OverlayThree should reflect second field change", 1, overlayThree.getCurrentTick()); - assertEquals("OverlayHotkeys should reflect second field change", 5, overlayHotkeys.getCurrentTick()); - } - public static void main(String[] args) throws Exception { ExternalPluginManager.loadBuiltin(VisualTicksPlugin.class);