diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java index 30087bb33a..1553bc904a 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java @@ -99,11 +99,9 @@ class ConfigInvocationHandler implements InvocationHandler } // Convert value to return type - Class returnType = method.getReturnType(); - try { - Object objectValue = ConfigManager.stringToObject(value, returnType); + Object objectValue = manager.stringToObject(value, method.getGenericReturnType()); cache.put(method, objectValue == null ? NULL : objectValue); return objectValue; } @@ -155,7 +153,7 @@ class ConfigInvocationHandler implements InvocationHandler } else { - String newValueStr = ConfigManager.objectToString(newValue); + String newValueStr = manager.objectToString(newValue); manager.setConfiguration(group.value(), item.keyName(), newValueStr); } return null; diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigItemDescriptor.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigItemDescriptor.java index d6ced58e18..922fb531a8 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigItemDescriptor.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigItemDescriptor.java @@ -24,13 +24,14 @@ */ package net.runelite.client.config; +import java.lang.reflect.Type; import lombok.Value; @Value public class ConfigItemDescriptor implements ConfigObject { private final ConfigItem item; - private final Class type; + private final Type type; private final Range range; private final Alpha alpha; private final Units units; diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index f06319c68f..1f14b5a504 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -30,6 +30,7 @@ import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableSet; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; +import com.google.gson.Gson; import java.awt.Color; import java.awt.Dimension; import java.awt.Point; @@ -43,7 +44,9 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; +import java.lang.reflect.Type; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.AtomicMoveNotSupportedException; @@ -120,6 +123,7 @@ public class ConfigManager private final File settingsFileInput; private final EventBus eventBus; private final OkHttpClient okHttpClient; + private final Gson gson; private AccountSession session; private ConfigClient configClient; @@ -143,13 +147,15 @@ public class ConfigManager ScheduledExecutorService scheduledExecutorService, EventBus eventBus, OkHttpClient okHttpClient, - @Nullable Client client) + @Nullable Client client, + Gson gson) { this.settingsFileInput = config; this.eventBus = eventBus; this.okHttpClient = okHttpClient; this.client = client; this.propertiesFile = getPropertiesFile(); + this.gson = gson; scheduledExecutorService.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS); } @@ -449,12 +455,12 @@ public class ConfigManager return properties.getProperty(getWholeKey(groupName, profile, key)); } - public T getConfiguration(String groupName, String key, Class clazz) + public T getConfiguration(String groupName, String key, Type clazz) { return getConfiguration(groupName, null, key, clazz); } - public T getRSProfileConfiguration(String groupName, String key, Class clazz) + public T getRSProfileConfiguration(String groupName, String key, Type clazz) { String rsProfileKey = this.rsProfileKey; if (rsProfileKey == null) @@ -465,14 +471,14 @@ public class ConfigManager return getConfiguration(groupName, rsProfileKey, key, clazz); } - public T getConfiguration(String groupName, String profile, String key, Class clazz) + public T getConfiguration(String groupName, String profile, String key, Type type) { String value = getConfiguration(groupName, profile, key); if (!Strings.isNullOrEmpty(value)) { try { - return (T) stringToObject(value, clazz); + return (T) stringToObject(value, type); } catch (Exception e) { @@ -525,17 +531,17 @@ public class ConfigManager eventBus.post(configChanged); } - public void setConfiguration(String groupName, String profile, String key, Object value) + public void setConfiguration(String groupName, String profile, String key, T value) { setConfiguration(groupName, profile, key, objectToString(value)); } - public void setConfiguration(String groupName, String key, Object value) + public void setConfiguration(String groupName, String key, T value) { setConfiguration(groupName, null, key, value); } - public void setRSProfileConfiguration(String groupName, String key, Object value) + public void setRSProfileConfiguration(String groupName, String key, T value) { String rsProfileKey = this.rsProfileKey; if (rsProfileKey == null) @@ -657,7 +663,7 @@ public class ConfigManager .filter(m -> m.getParameterCount() == 0 && m.isAnnotationPresent(ConfigItem.class)) .map(m -> new ConfigItemDescriptor( m.getDeclaredAnnotation(ConfigItem.class), - m.getReturnType(), + m.getGenericReturnType(), m.getDeclaredAnnotation(Range.class), m.getDeclaredAnnotation(Alpha.class), m.getDeclaredAnnotation(Units.class) @@ -714,7 +720,7 @@ public class ConfigManager { // This checks if it is set and is also unmarshallable to the correct type; so // we will overwrite invalid config values with the default - Object current = getConfiguration(group.value(), item.keyName(), method.getReturnType()); + Object current = getConfiguration(group.value(), item.keyName(), method.getGenericReturnType()); if (current != null) { continue; // something else is already set @@ -748,7 +754,7 @@ public class ConfigManager } } - static Object stringToObject(String str, Class type) + Object stringToObject(String str, Type type) { if (type == boolean.class || type == Boolean.class) { @@ -789,7 +795,7 @@ public class ConfigManager int height = Integer.parseInt(splitStr[3]); return new Rectangle(x, y, width, height); } - if (type.isEnum()) + if (type instanceof Class && ((Class) type).isEnum()) { return Enum.valueOf((Class) type, str); } @@ -824,11 +830,19 @@ public class ConfigManager { return Base64.getUrlDecoder().decode(str); } + if (type instanceof ParameterizedType) + { + ParameterizedType parameterizedType = (ParameterizedType) type; + if (parameterizedType.getRawType() == Set.class) + { + return gson.fromJson(str, parameterizedType); + } + } return str; } @Nullable - static String objectToString(Object object) + String objectToString(Object object) { if (object instanceof Color) { @@ -875,6 +889,10 @@ public class ConfigManager { return Base64.getUrlEncoder().encodeToString((byte[]) object); } + if (object instanceof Set) + { + return gson.toJson(object, Set.class); + } return object == null ? null : object.toString(); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java index 9e43f757b6..fc7da81c7f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java @@ -24,8 +24,10 @@ */ package net.runelite.client.plugins.config; +import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.common.collect.ComparisonChain; +import com.google.common.collect.Sets; import com.google.common.primitives.Ints; import java.awt.BorderLayout; import java.awt.Color; @@ -37,8 +39,12 @@ import java.awt.event.ItemEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; +import java.lang.reflect.ParameterizedType; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeMap; import javax.inject.Inject; import javax.swing.BorderFactory; @@ -49,6 +55,7 @@ import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFormattedTextField; import javax.swing.JLabel; +import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; @@ -56,6 +63,8 @@ import javax.swing.JPasswordField; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTextArea; +import javax.swing.ListCellRenderer; +import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; @@ -97,6 +106,7 @@ import net.runelite.client.util.ColorUtil; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.SwingUtil; import net.runelite.client.util.Text; +import org.apache.commons.lang3.ArrayUtils; @Slf4j class ConfigPanel extends PluginPanel @@ -111,27 +121,6 @@ class ConfigPanel extends PluginPanel private static final Map sectionExpandStates = new HashMap<>(); - private final FixedWidthPanel mainPanel; - private final JLabel title; - private final PluginToggleButton pluginToggle; - - @Inject - private PluginListPanel pluginList; - - @Inject - private ConfigManager configManager; - - @Inject - private PluginManager pluginManager; - - @Inject - private ExternalPluginManager externalPluginManager; - - @Inject - private ColorPickerManager colorPickerManager; - - private PluginConfigurationDescriptor pluginConfig = null; - static { final BufferedImage backIcon = ImageUtil.loadImageResource(ConfigPanel.class, "config_back_icon.png"); @@ -147,10 +136,32 @@ class ConfigPanel extends PluginPanel SECTION_RETRACT_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(sectionExpandIcon, -100)); } - public ConfigPanel() + private final PluginListPanel pluginList; + private final ConfigManager configManager; + private final PluginManager pluginManager; + private final ExternalPluginManager externalPluginManager; + private final ColorPickerManager colorPickerManager; + + private final ListCellRenderer> listCellRenderer = new ComboBoxListRenderer<>(); + + private final FixedWidthPanel mainPanel; + private final JLabel title; + private final PluginToggleButton pluginToggle; + + private PluginConfigurationDescriptor pluginConfig = null; + + @Inject + private ConfigPanel(PluginListPanel pluginList, ConfigManager configManager, PluginManager pluginManager, + ExternalPluginManager externalPluginManager, ColorPickerManager colorPickerManager) { super(false); + this.pluginList = pluginList; + this.configManager = configManager; + this.pluginManager = pluginManager; + this.externalPluginManager = externalPluginManager; + this.colorPickerManager = colorPickerManager; + setLayout(new BorderLayout()); setBackground(ColorScheme.DARK_GRAY_COLOR); @@ -359,7 +370,7 @@ class ConfigPanel extends PluginPanel { item.add(createDimension(cd, cid), BorderLayout.EAST); } - else if (cid.getType().isEnum()) + else if (cid.getType() instanceof Class && ((Class) cid.getType()).isEnum()) { item.add(createComboBox(cd, cid), BorderLayout.EAST); } @@ -367,6 +378,14 @@ class ConfigPanel extends PluginPanel { item.add(createKeybind(cd, cid), BorderLayout.EAST); } + else if (cid.getType() instanceof ParameterizedType) + { + ParameterizedType parameterizedType = (ParameterizedType) cid.getType(); + if (parameterizedType.getRawType() == Set.class) + { + item.add(createList(cd, cid), BorderLayout.EAST); + } + } JPanel section = sectionWidgets.get(cid.getItem().section()); if (section == null) @@ -581,7 +600,7 @@ class ConfigPanel extends PluginPanel // set renderer prior to calling box.getPreferredSize(), since it will invoke the renderer // to build components for each combobox element in order to compute the display size of the // combobox - box.setRenderer(new ComboBoxListRenderer<>()); + box.setRenderer(listCellRenderer); box.setPreferredSize(new Dimension(box.getPreferredSize().width, 25)); box.setForeground(Color.WHITE); box.setFocusable(false); @@ -594,7 +613,7 @@ class ConfigPanel extends PluginPanel } catch (IllegalArgumentException ex) { - log.debug("invalid seleced item", ex); + log.debug("invalid selected item", ex); } box.addItemListener(e -> { @@ -628,6 +647,34 @@ class ConfigPanel extends PluginPanel return button; } + private JList> createList(ConfigDescriptor cd, ConfigItemDescriptor cid) + { + ParameterizedType parameterizedType = (ParameterizedType) cid.getType(); + Class type = (Class) parameterizedType.getActualTypeArguments()[0]; + Set set = configManager.getConfiguration(cd.getGroup().value(), null, + cid.getItem().keyName(), parameterizedType); + + JList> list = new JList>(type.getEnumConstants()); // NOPMD: UseDiamondOperator + list.setCellRenderer(listCellRenderer); + list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + list.setLayoutOrientation(JList.VERTICAL); + list.setSelectedIndices( + MoreObjects.firstNonNull(set, Collections.emptySet()) + .stream() + .mapToInt(e -> ArrayUtils.indexOf(type.getEnumConstants(), e)) + .toArray()); + list.addFocusListener(new FocusAdapter() + { + @Override + public void focusLost(FocusEvent e) + { + changeConfiguration(list, cd, cid); + } + }); + + return list; + } + private void changeConfiguration(Component component, ConfigDescriptor cd, ConfigItemDescriptor cid) { final ConfigItem configItem = cid.getItem(); @@ -675,6 +722,13 @@ class ConfigPanel extends PluginPanel HotkeyButton hotkeyButton = (HotkeyButton) component; configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), hotkeyButton.getValue()); } + else if (component instanceof JList) + { + JList list = (JList) component; + List selectedValues = list.getSelectedValuesList(); + + configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), Sets.newHashSet(selectedValues)); + } } @Override diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java index b73700134c..7cb057f8e7 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java @@ -59,9 +59,9 @@ public final class ComboBoxListRenderer extends JLabel implements ListCellRen setBorder(new EmptyBorder(5, 5, 5, 0)); String text; - if (o instanceof Enum) + if (o instanceof Enum) { - text = Text.titleCase((Enum) o); + text = Text.titleCase((Enum) o); } else {