configs: reimplement rework

This commit is contained in:
Justin
2021-12-09 09:11:59 +11:00
parent f97aaf3a5b
commit 2fd6ec9d8c
4 changed files with 332 additions and 565 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
@@ -44,6 +45,7 @@ import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
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;
@@ -129,6 +131,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;
@@ -153,13 +156,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);
}
@@ -413,17 +418,12 @@ public class ConfigManager
return properties.getProperty(getWholeKey(groupName, profile, key));
}
public <T> T getConfiguration(String groupName, String key, Class<T> clazz)
public <T> T getConfiguration(String groupName, String key, Type clazz)
{
return getConfiguration(groupName, null, key, clazz);
}
public <T> Object getConfiguration(String groupName, String key, Type clazz)
{
return getConfiguration(groupName, null, key, clazz);
}
public <T> T getRSProfileConfiguration(String groupName, String key, Class<T> clazz)
public <T> T getRSProfileConfiguration(String groupName, String key, Type clazz)
{
String rsProfileKey = this.rsProfileKey;
if (rsProfileKey == null)
@@ -434,31 +434,14 @@ public class ConfigManager
return getConfiguration(groupName, rsProfileKey, key, clazz);
}
public <T> T getConfiguration(String groupName, String profile, String key, Type clazz)
public <T> 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, (Class<?>) clazz);
}
catch (Exception e)
{
log.warn("Unable to unmarshal {} ", getWholeKey(groupName, profile, key), e);
}
}
return null;
}
public <T> T getConfiguration(String groupName, String profile, String key, Class<T> clazz)
{
String value = getConfiguration(groupName, profile, key);
if (!Strings.isNullOrEmpty(value))
{
try
{
return (T) stringToObject(value, clazz);
return (T) stringToObject(value, type);
}
catch (Exception e)
{
@@ -511,12 +494,12 @@ public class ConfigManager
eventBus.post(configChanged);
}
public void setConfiguration(String groupName, String profile, String key, Object value)
public <T> 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 <T> void setConfiguration(String groupName, String key, T value)
{
// do not save consumers for buttons, they cannot be changed anyway
if (value instanceof Consumer)
@@ -527,7 +510,7 @@ public class ConfigManager
setConfiguration(groupName, null, key, value);
}
public void setRSProfileConfiguration(String groupName, String key, Object value)
public <T> void setRSProfileConfiguration(String groupName, String key, T value)
{
String rsProfileKey = this.rsProfileKey;
if (rsProfileKey == null)
@@ -646,7 +629,7 @@ public class ConfigManager
.collect(Collectors.toList());
final List<ConfigTitleDescriptor> titles = getAllDeclaredInterfaceFields(inter).stream()
.filter(m -> m.isAnnotationPresent(ConfigTitle.class) && m.getType() == String.class)
.filter(m -> m.isAnnotationPresent(ConfigTitle.class) && m.getClass().equals(String.class))
.map(m ->
{
try
@@ -673,7 +656,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)
@@ -748,7 +731,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
@@ -783,7 +766,7 @@ public class ConfigManager
}
}
static Object stringToObject(String str, Class<?> type)
Object stringToObject(String str, Type type)
{
if (type == boolean.class || type == Boolean.class)
{
@@ -824,7 +807,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<? extends Enum>) type, str);
}
@@ -859,6 +842,14 @@ 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);
}
}
if (type == EnumSet.class)
{
try
@@ -898,7 +889,7 @@ public class ConfigManager
}
@Nullable
static String objectToString(Object object)
String objectToString(Object object)
{
if (object instanceof Color)
{
@@ -945,6 +936,10 @@ public class ConfigManager
{
return Base64.getUrlEncoder().encodeToString((byte[]) object);
}
if (object instanceof Set)
{
return gson.toJson(object, Set.class);
}
if (object instanceof EnumSet)
{
if (((EnumSet) object).size() == 0)
@@ -954,7 +949,6 @@ public class ConfigManager
return ((EnumSet) object).toArray()[0].getClass().getCanonicalName() + "{" + object.toString() + "}";
}
return object == null ? null : object.toString();
}

View File

@@ -24,68 +24,36 @@
*/
package net.runelite.client.plugins.config;
import com.google.common.base.MoreObjects;
import com.google.common.base.Splitter;
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.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.lang.reflect.ParameterizedType;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.*;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.basic.BasicSpinnerUI;
import javax.swing.text.JTextComponent;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.events.ConfigButtonClicked;
import net.runelite.client.config.Button;
import net.runelite.client.config.ConfigDescriptor;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@@ -100,7 +68,6 @@ import net.runelite.client.config.Keybind;
import net.runelite.client.config.ModifierlessKeybind;
import net.runelite.client.config.Range;
import net.runelite.client.config.Units;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ExternalPluginsChanged;
import net.runelite.client.events.PluginChanged;
@@ -115,15 +82,14 @@ import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.ColorJButton;
import net.runelite.client.ui.components.ComboBoxListRenderer;
import net.runelite.client.ui.components.ToggleButton;
import net.runelite.client.ui.components.colorpicker.ColorPickerManager;
import net.runelite.client.ui.components.colorpicker.RuneliteColorPicker;
import net.runelite.client.util.ColorUtil;
import net.runelite.client.util.DeferredDocumentChangedListener;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.LinkBrowser;
import net.runelite.client.util.SwingUtil;
import net.runelite.client.util.Text;
import org.apache.commons.lang3.ArrayUtils;
@Slf4j
class ConfigPanel extends PluginPanel
@@ -138,33 +104,6 @@ class ConfigPanel extends PluginPanel
private static final Map<ConfigSectionDescriptor, Boolean> 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 OPRSExternalPluginManager oprsExternalPluginManager;
@Inject
private ColorPickerManager colorPickerManager;
@Inject
private EventBus eventBus;
private PluginConfigurationDescriptor pluginConfig = null;
static
{
final BufferedImage backIcon = ImageUtil.loadImageResource(ConfigPanel.class, "config_back_icon.png");
@@ -180,10 +119,36 @@ 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<Enum<?>> listCellRenderer = new ComboBoxListRenderer<>();
private final FixedWidthPanel mainPanel;
private final JLabel title;
private final PluginToggleButton pluginToggle;
private PluginConfigurationDescriptor pluginConfig = null;
@Inject
private OPRSExternalPluginManager oprsExternalPluginManager;
@Inject
private ConfigPanel(PluginListPanel pluginList, ConfigManager configManager, PluginManager pluginManager,
ExternalPluginManager externalPluginManager, ColorPickerManager colorPickerManager, OPRSExternalPluginManager oprsExternalPluginManager)
{
super(false);
this.pluginList = pluginList;
this.configManager = configManager;
this.pluginManager = pluginManager;
this.externalPluginManager = externalPluginManager;
this.colorPickerManager = colorPickerManager;
this.oprsExternalPluginManager = oprsExternalPluginManager;
setLayout(new BorderLayout());
setBackground(ColorScheme.DARK_GRAY_COLOR);
@@ -462,436 +427,63 @@ class ConfigPanel extends PluginPanel
configEntryName.setToolTipText("<html>" + name + ":<br>" + cid.getItem().description() + "</html>");
PluginListItem.addLabelPopupMenu(configEntryName, createResetMenuItem(pluginConfig, cid));
item.add(configEntryName, BorderLayout.CENTER);
item.setName(cid.getItem().keyName());
if (cid.getType() == Button.class)
{
try
{
ConfigItem cidItem = cid.getItem();
JButton button = new JButton(cidItem.name());
button.addActionListener((e) ->
{
ConfigButtonClicked event = new ConfigButtonClicked();
event.setGroup(cd.getGroup().value());
event.setKey(cid.getItem().keyName());
eventBus.post(event);
});
item.add(button);
}
catch (Exception ex)
{
log.error("Adding action listener failed: {}", ex.getMessage());
ex.printStackTrace();
}
}
if (cid.getType() == boolean.class)
{
JCheckBox checkbox = new ToggleButton();
checkbox.setPreferredSize(new Dimension(26, 25));
checkbox.setSelected(Boolean.parseBoolean(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName())));
checkbox.addActionListener(ae -> changeConfiguration(checkbox, cd, cid));
item.add(checkbox, BorderLayout.EAST);
item.add(createCheckbox(cd, cid), BorderLayout.EAST);
}
if (cid.getType().isAssignableFrom(Consumer.class))
else if (cid.getType() == int.class)
{
item.remove(configEntryName);
JButton button = new JButton(cid.getItem().name());
button.addActionListener((e) ->
{
log.debug("Running consumer: {}.{}", cd.getGroup().value(), cid.getItem().keyName());
configManager.getConsumer(cd.getGroup().value(), cid.getItem().keyName()).accept(pluginConfig.getPlugin());
});
item.add(button, BorderLayout.CENTER);
item.add(createIntSpinner(cd, cid), BorderLayout.EAST);
}
if (cid.getType() == int.class)
{
int value = Integer.parseInt(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName()));
Units units = cid.getUnits();
Range range = cid.getRange();
int min = 0, max = Integer.MAX_VALUE;
if (range != null)
{
min = range.min();
max = range.max();
}
// Config may previously have been out of range
value = Ints.constrainToRange(value, min, max);
if (max < Integer.MAX_VALUE)
{
JLabel sliderValueLabel = new JLabel();
JSlider slider = new JSlider(min, max, value);
slider.setBackground(ColorScheme.DARK_GRAY_COLOR);
if (units != null)
{
sliderValueLabel.setText(slider.getValue() + units.value());
}
else
{
sliderValueLabel.setText(String.valueOf(slider.getValue()));
}
slider.setPreferredSize(new Dimension(80, 25));
slider.addChangeListener((l) ->
{
if (units != null)
{
sliderValueLabel.setText(slider.getValue() + units.value());
}
else
{
sliderValueLabel.setText(String.valueOf(slider.getValue()));
}
if (!slider.getValueIsAdjusting())
{
changeConfiguration(slider, cd, cid);
}
}
);
SpinnerModel model = new SpinnerNumberModel(value, min, max, 1);
JSpinner spinner = new JSpinner(model);
Component editor = spinner.getEditor();
JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField();
spinnerTextField.setColumns(SPINNER_FIELD_WIDTH);
spinner.setUI(new BasicSpinnerUI()
{
protected Component createNextButton()
{
return null;
}
protected Component createPreviousButton()
{
return null;
}
});
JPanel subPanel = new JPanel();
subPanel.setPreferredSize(new Dimension(110, 25));
subPanel.setLayout(new BorderLayout());
spinner.addChangeListener((ce) ->
{
changeConfiguration(spinner, cd, cid);
if (units != null)
{
sliderValueLabel.setText(spinner.getValue() + units.value());
}
else
{
sliderValueLabel.setText(String.valueOf(spinner.getValue()));
}
slider.setValue((Integer) spinner.getValue());
subPanel.add(sliderValueLabel, BorderLayout.WEST);
subPanel.add(slider, BorderLayout.EAST);
subPanel.remove(spinner);
validate();
repaint();
});
sliderValueLabel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
spinner.setValue(slider.getValue());
subPanel.remove(sliderValueLabel);
subPanel.remove(slider);
subPanel.add(spinner, BorderLayout.EAST);
validate();
repaint();
final JTextField tf = ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField();
tf.requestFocusInWindow();
SwingUtilities.invokeLater(tf::selectAll);
}
});
subPanel.add(sliderValueLabel, BorderLayout.WEST);
subPanel.add(slider, BorderLayout.EAST);
item.add(subPanel, BorderLayout.EAST);
}
else
{
SpinnerModel model = new SpinnerNumberModel(value, min, max, 1);
JSpinner spinner = new JSpinner(model);
Component editor = spinner.getEditor();
JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField();
spinnerTextField.setColumns(SPINNER_FIELD_WIDTH);
spinner.addChangeListener(ce -> changeConfiguration(spinner, cd, cid));
if (units != null)
{
spinnerTextField.setFormatterFactory(new UnitFormatterFactory(units));
}
item.add(spinner, BorderLayout.EAST);
}
}
else if (cid.getType() == double.class)
{
double value = configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName(), double.class);
SpinnerModel model = new SpinnerNumberModel(value, 0, Double.MAX_VALUE, 0.1);
JSpinner spinner = new JSpinner(model);
Component editor = spinner.getEditor();
JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField();
spinnerTextField.setColumns(SPINNER_FIELD_WIDTH);
spinner.addChangeListener(ce -> changeConfiguration(spinner, cd, cid));
item.add(spinner, BorderLayout.EAST);
item.add(createDoubleSpinner(cd, cid), BorderLayout.EAST);
}
if (cid.getType() == String.class)
else if (cid.getType() == String.class)
{
JTextComponent textField;
if (cid.getItem().secret())
{
textField = new JPasswordField();
}
else
{
final JTextArea textArea = new JTextArea();
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textField = textArea;
}
textField.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
textField.setText(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName()));
textField.addFocusListener(new FocusAdapter()
{
@Override
public void focusLost(FocusEvent e)
{
changeConfiguration(textField, cd, cid);
}
});
if (cid.getItem().parse())
{
JLabel parsingLabel = new JLabel();
parsingLabel.setHorizontalAlignment(SwingConstants.CENTER);
parsingLabel.setPreferredSize(new Dimension(PANEL_WIDTH, 15));
DeferredDocumentChangedListener listener = new DeferredDocumentChangedListener();
listener.addChangeListener(e ->
{
if (cid.getItem().parse())
{
parseLabel(cid.getItem(), parsingLabel, textField.getText());
}
});
textField.getDocument().addDocumentListener(listener);
item.add(configEntryName, BorderLayout.NORTH);
item.add(textField, BorderLayout.CENTER);
parseLabel(cid.getItem(), parsingLabel, textField.getText());
item.add(parsingLabel, BorderLayout.SOUTH);
}
else
{
item.add(textField, BorderLayout.SOUTH);
}
item.add(createTextField(cd, cid), BorderLayout.SOUTH);
}
if (cid.getType() == Color.class)
else if (cid.getType() == Color.class)
{
Color existing = configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName(), Color.class);
ColorJButton colorPickerBtn;
boolean alphaHidden = cid.getAlpha() == null;
if (existing == null)
{
colorPickerBtn = new ColorJButton("Pick a color", Color.BLACK);
}
else
{
String colorHex = "#" + (alphaHidden ? ColorUtil.colorToHexCode(existing) : ColorUtil.colorToAlphaHexCode(existing)).toUpperCase();
colorPickerBtn = new ColorJButton(colorHex, existing);
}
colorPickerBtn.setFocusable(false);
colorPickerBtn.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
RuneliteColorPicker colorPicker = colorPickerManager.create(
SwingUtilities.windowForComponent(ConfigPanel.this),
colorPickerBtn.getColor(),
cid.getItem().name(),
alphaHidden);
colorPicker.setLocation(getLocationOnScreen());
colorPicker.setOnColorChange(c ->
{
colorPickerBtn.setColor(c);
colorPickerBtn.setText("#" + (alphaHidden ? ColorUtil.colorToHexCode(c) : ColorUtil.colorToAlphaHexCode(c)).toUpperCase());
});
colorPicker.setOnClose(c -> changeConfiguration(colorPicker, cd, cid));
colorPicker.setVisible(true);
}
});
item.add(colorPickerBtn, BorderLayout.EAST);
item.add(createColorPicker(cd, cid), BorderLayout.EAST);
}
if (cid.getType() == Dimension.class)
else if (cid.getType() == Dimension.class)
{
JPanel dimensionPanel = new JPanel();
dimensionPanel.setLayout(new BorderLayout());
String str = configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName());
String[] splitStr = str.split("x");
int width = Integer.parseInt(splitStr[0]);
int height = Integer.parseInt(splitStr[1]);
SpinnerModel widthModel = new SpinnerNumberModel(width, 0, Integer.MAX_VALUE, 1);
JSpinner widthSpinner = new JSpinner(widthModel);
Component widthEditor = widthSpinner.getEditor();
JFormattedTextField widthSpinnerTextField = ((JSpinner.DefaultEditor) widthEditor).getTextField();
widthSpinnerTextField.setColumns(4);
SpinnerModel heightModel = new SpinnerNumberModel(height, 0, Integer.MAX_VALUE, 1);
JSpinner heightSpinner = new JSpinner(heightModel);
Component heightEditor = heightSpinner.getEditor();
JFormattedTextField heightSpinnerTextField = ((JSpinner.DefaultEditor) heightEditor).getTextField();
heightSpinnerTextField.setColumns(4);
ChangeListener listener = e ->
configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), widthSpinner.getValue() + "x" + heightSpinner.getValue());
widthSpinner.addChangeListener(listener);
heightSpinner.addChangeListener(listener);
dimensionPanel.add(widthSpinner, BorderLayout.WEST);
dimensionPanel.add(new JLabel(" x "), BorderLayout.CENTER);
dimensionPanel.add(heightSpinner, BorderLayout.EAST);
item.add(dimensionPanel, BorderLayout.EAST);
item.add(createDimension(cd, cid), BorderLayout.EAST);
}
if (cid.getType().isEnum())
else if (cid.getType() instanceof Class && ((Class<?>) cid.getType()).isEnum())
{
Class<? extends Enum> type = (Class<? extends Enum>) cid.getType();
JComboBox<Enum<?>> box = new JComboBox<Enum<?>>(type.getEnumConstants()); // NOPMD: UseDiamondOperator
// 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.setPreferredSize(new Dimension(box.getPreferredSize().width, 25));
box.setForeground(Color.WHITE);
box.setFocusable(false);
try
{
Enum<?> selectedItem = Enum.valueOf(type, configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName()));
box.setSelectedItem(selectedItem);
box.setToolTipText(Text.titleCase(selectedItem));
}
catch (IllegalArgumentException ex)
{
log.debug("invalid seleced item", ex);
}
box.addItemListener(e ->
{
if (e.getStateChange() == ItemEvent.SELECTED)
{
changeConfiguration(box, cd, cid);
box.setToolTipText(Text.titleCase((Enum<?>) box.getSelectedItem()));
}
});
item.add(box, BorderLayout.EAST);
item.add(createComboBox(cd, cid), BorderLayout.EAST);
}
if (cid.getType() == Keybind.class || cid.getType() == ModifierlessKeybind.class)
else if (cid.getType() == Keybind.class || cid.getType() == ModifierlessKeybind.class)
{
Keybind startingValue = configManager.getConfiguration(cd.getGroup().value(),
cid.getItem().keyName(),
(Class<? extends Keybind>) cid.getType());
HotkeyButton button = new HotkeyButton(startingValue, cid.getType() == ModifierlessKeybind.class);
button.addFocusListener(new FocusAdapter()
{
@Override
public void focusLost(FocusEvent e)
{
changeConfiguration(button, cd, cid);
}
});
item.add(button, BorderLayout.EAST);
item.add(createKeybind(cd, cid), BorderLayout.EAST);
}
if (cid.getType() == EnumSet.class)
else if (cid.getType() instanceof ParameterizedType)
{
Class enumType = cid.getItem().enumClass();
EnumSet enumSet = configManager.getConfiguration(cd.getGroup().value(),
cid.getItem().keyName(), EnumSet.class);
if (enumSet == null || enumSet.contains(null))
ParameterizedType parameterizedType = (ParameterizedType) cid.getType();
if (parameterizedType.getRawType() == Set.class)
{
enumSet = EnumSet.noneOf(enumType);
item.add(createList(cd, cid), BorderLayout.EAST);
}
}
else if (cid.getType() instanceof EnumSet)
{
ParameterizedType parameterizedType = (ParameterizedType) cid.getType();
if (parameterizedType.getRawType() == EnumSet.class)
{
item.add(createList(cd, cid), BorderLayout.EAST);
}
JPanel enumsetLayout = new JPanel(new GridLayout(0, 2));
List<JCheckBox> jcheckboxes = new ArrayList<>();
for (Object obj : enumType.getEnumConstants())
{
String option = String.valueOf(obj).toLowerCase().replace("_", " ");
JCheckBox checkbox = new ToggleButton(option);
checkbox.setBackground(ColorScheme.DARK_GRAY_COLOR);
checkbox.setSelected(enumSet.contains(obj));
jcheckboxes.add(checkbox);
enumsetLayout.add(checkbox);
}
jcheckboxes.forEach(checkbox -> checkbox.addActionListener(ae -> changeConfiguration(jcheckboxes, cd, cid)));
item.add(enumsetLayout, BorderLayout.SOUTH);
}
JPanel section = sectionWidgets.get(cid.getItem().section());
JPanel title = titleWidgets.get(cid.getItem().title());
if (section != null)
if (section == null)
{
section.add(item);
}
else if (title != null)
{
title.add(item);
topLevelPanels.put(cid, item);
}
else
{
topLevelPanels.put(cid, item);
section.add(item);
}
}
@@ -927,69 +519,249 @@ class ConfigPanel extends PluginPanel
revalidate();
}
private Boolean parse(ConfigItem item, String value)
private JCheckBox createCheckbox(ConfigDescriptor cd, ConfigItemDescriptor cid)
{
try
{
Method parse = item.clazz().getMethod(item.method(), String.class);
return (boolean) parse.invoke(null, value);
}
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex)
{
log.error("Parsing failed: {}", ex.getMessage());
}
return null;
JCheckBox checkbox = new JCheckBox();
checkbox.setBackground(ColorScheme.LIGHT_GRAY_COLOR);
checkbox.setSelected(Boolean.parseBoolean(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName())));
checkbox.addActionListener(ae -> changeConfiguration(checkbox, cd, cid));
return checkbox;
}
private void parseLabel(ConfigItem item, JLabel label, String value)
private JSpinner createIntSpinner(ConfigDescriptor cd, ConfigItemDescriptor cid)
{
Boolean result = parse(item, value);
int value = Integer.parseInt(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName()));
if (result == null)
Range range = cid.getRange();
int min = 0, max = Integer.MAX_VALUE;
if (range != null)
{
label.setForeground(Color.RED);
label.setText("Parsing failed");
min = range.min();
max = range.max();
}
else if (result)
// Config may previously have been out of range
value = Ints.constrainToRange(value, min, max);
SpinnerModel model = new SpinnerNumberModel(value, min, max, 1);
JSpinner spinner = new JSpinner(model);
Component editor = spinner.getEditor();
JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField();
spinnerTextField.setColumns(SPINNER_FIELD_WIDTH);
spinner.addChangeListener(ce -> changeConfiguration(spinner, cd, cid));
Units units = cid.getUnits();
if (units != null)
{
label.setForeground(Color.GREEN);
label.setText("Valid input");
spinnerTextField.setFormatterFactory(new UnitFormatterFactory(units));
}
return spinner;
}
private JSpinner createDoubleSpinner(ConfigDescriptor cd, ConfigItemDescriptor cid)
{
double value = configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName(), double.class);
SpinnerModel model = new SpinnerNumberModel(value, 0, Double.MAX_VALUE, 0.1);
JSpinner spinner = new JSpinner(model);
Component editor = spinner.getEditor();
JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField();
spinnerTextField.setColumns(SPINNER_FIELD_WIDTH);
spinner.addChangeListener(ce -> changeConfiguration(spinner, cd, cid));
return spinner;
}
private JTextComponent createTextField(ConfigDescriptor cd, ConfigItemDescriptor cid)
{
JTextComponent textField;
if (cid.getItem().secret())
{
textField = new JPasswordField();
}
else
{
label.setForeground(Color.RED);
label.setText("Invalid input");
final JTextArea textArea = new JTextArea();
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textField = textArea;
}
}
private void changeConfiguration(List<JCheckBox> components, ConfigDescriptor cd, ConfigItemDescriptor cid)
{
Class<? extends Enum> enumType = cid.getItem().enumClass();
EnumSet enumSet = configManager.getConfiguration(cd.getGroup().value(),
cid.getItem().keyName(), EnumSet.class);
if (enumSet == null)
textField.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
textField.setText(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName()));
textField.addFocusListener(new FocusAdapter()
{
//noinspection unchecked
enumSet = EnumSet.noneOf(enumType);
}
enumSet.clear();
EnumSet finalEnumSet = enumSet;
//noinspection unchecked
components.forEach(value ->
{
if (value.isSelected())
@Override
public void focusLost(FocusEvent e)
{
finalEnumSet.add(Enum.valueOf(cid.getItem().enumClass(), String.valueOf(value.getText()).toUpperCase().replace(" ", "_")));
changeConfiguration(textField, cd, cid);
}
});
configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), finalEnumSet);
return textField;
}
rebuild();
private ColorJButton createColorPicker(ConfigDescriptor cd, ConfigItemDescriptor cid)
{
Color existing = configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName(), Color.class);
ColorJButton colorPickerBtn;
boolean alphaHidden = cid.getAlpha() == null;
if (existing == null)
{
colorPickerBtn = new ColorJButton("Pick a color", Color.BLACK);
}
else
{
String colorHex = "#" + (alphaHidden ? ColorUtil.colorToHexCode(existing) : ColorUtil.colorToAlphaHexCode(existing)).toUpperCase();
colorPickerBtn = new ColorJButton(colorHex, existing);
}
colorPickerBtn.setFocusable(false);
colorPickerBtn.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
RuneliteColorPicker colorPicker = colorPickerManager.create(
SwingUtilities.windowForComponent(ConfigPanel.this),
colorPickerBtn.getColor(),
cid.getItem().name(),
alphaHidden);
colorPicker.setLocation(getLocationOnScreen());
colorPicker.setOnColorChange(c ->
{
colorPickerBtn.setColor(c);
colorPickerBtn.setText("#" + (alphaHidden ? ColorUtil.colorToHexCode(c) : ColorUtil.colorToAlphaHexCode(c)).toUpperCase());
});
colorPicker.setOnClose(c -> changeConfiguration(colorPicker, cd, cid));
colorPicker.setVisible(true);
}
});
return colorPickerBtn;
}
private JPanel createDimension(ConfigDescriptor cd, ConfigItemDescriptor cid)
{
JPanel dimensionPanel = new JPanel();
dimensionPanel.setLayout(new BorderLayout());
String str = configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName());
String[] splitStr = str.split("x");
int width = Integer.parseInt(splitStr[0]);
int height = Integer.parseInt(splitStr[1]);
SpinnerModel widthModel = new SpinnerNumberModel(width, 0, Integer.MAX_VALUE, 1);
JSpinner widthSpinner = new JSpinner(widthModel);
Component widthEditor = widthSpinner.getEditor();
JFormattedTextField widthSpinnerTextField = ((JSpinner.DefaultEditor) widthEditor).getTextField();
widthSpinnerTextField.setColumns(4);
SpinnerModel heightModel = new SpinnerNumberModel(height, 0, Integer.MAX_VALUE, 1);
JSpinner heightSpinner = new JSpinner(heightModel);
Component heightEditor = heightSpinner.getEditor();
JFormattedTextField heightSpinnerTextField = ((JSpinner.DefaultEditor) heightEditor).getTextField();
heightSpinnerTextField.setColumns(4);
ChangeListener listener = e ->
configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), widthSpinner.getValue() + "x" + heightSpinner.getValue());
widthSpinner.addChangeListener(listener);
heightSpinner.addChangeListener(listener);
dimensionPanel.add(widthSpinner, BorderLayout.WEST);
dimensionPanel.add(new JLabel(" x "), BorderLayout.CENTER);
dimensionPanel.add(heightSpinner, BorderLayout.EAST);
return dimensionPanel;
}
private JComboBox<Enum<?>> createComboBox(ConfigDescriptor cd, ConfigItemDescriptor cid)
{
Class<? extends Enum> type = (Class<? extends Enum>) cid.getType();
JComboBox<Enum<?>> box = new JComboBox<Enum<?>>(type.getEnumConstants()); // NOPMD: UseDiamondOperator
// 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(listCellRenderer);
box.setPreferredSize(new Dimension(box.getPreferredSize().width, 25));
box.setForeground(Color.WHITE);
box.setFocusable(false);
try
{
Enum<?> selectedItem = Enum.valueOf(type, configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName()));
box.setSelectedItem(selectedItem);
box.setToolTipText(Text.titleCase(selectedItem));
}
catch (IllegalArgumentException ex)
{
log.debug("invalid selected item", ex);
}
box.addItemListener(e ->
{
if (e.getStateChange() == ItemEvent.SELECTED)
{
changeConfiguration(box, cd, cid);
box.setToolTipText(Text.titleCase((Enum<?>) box.getSelectedItem()));
}
});
return box;
}
private HotkeyButton createKeybind(ConfigDescriptor cd, ConfigItemDescriptor cid)
{
Keybind startingValue = configManager.getConfiguration(cd.getGroup().value(),
cid.getItem().keyName(),
(Class<? extends Keybind>) cid.getType());
HotkeyButton button = new HotkeyButton(startingValue, cid.getType() == ModifierlessKeybind.class);
button.addFocusListener(new FocusAdapter()
{
@Override
public void focusLost(FocusEvent e)
{
changeConfiguration(button, cd, cid);
}
});
return button;
}
private JList<Enum<?>> createList(ConfigDescriptor cd, ConfigItemDescriptor cid)
{
ParameterizedType parameterizedType = (ParameterizedType) cid.getType();
Class<? extends Enum> type = (Class<? extends Enum>) parameterizedType.getActualTypeArguments()[0];
Set<? extends Enum> set = configManager.getConfiguration(cd.getGroup().value(), null,
cid.getItem().keyName(), parameterizedType);
JList<Enum<?>> list = new JList<Enum<?>>(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)
@@ -1019,11 +791,6 @@ class ConfigPanel extends PluginPanel
JSpinner spinner = (JSpinner) component;
configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), "" + spinner.getValue());
}
else if (component instanceof JSlider)
{
JSlider slider = (JSlider) component;
configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), slider.getValue());
}
else if (component instanceof JTextComponent)
{
JTextComponent textField = (JTextComponent) component;
@@ -1044,6 +811,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));
}
enableDisable(component, cid);
rebuild();
@@ -1121,7 +895,7 @@ class ConfigPanel extends PluginPanel
{
show = Boolean.parseBoolean(configManager.getConfiguration(cd.getGroup().value(), cid2.getItem().keyName()));
}
else if (cid2.getType().isEnum())
else if (cid2.getType() instanceof Class && ((Class<?>) cid2.getType()).isEnum())
{
Class<? extends Enum> type = (Class<? extends Enum>) cid2.getType();
try