config panel: change plugin search to use plugin name + tags
This commit is contained in:
@@ -29,7 +29,6 @@ import java.awt.BorderLayout;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.GridLayout;
|
|
||||||
import java.awt.event.FocusAdapter;
|
import java.awt.event.FocusAdapter;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.ItemEvent;
|
import java.awt.event.ItemEvent;
|
||||||
@@ -37,11 +36,10 @@ import java.awt.event.MouseAdapter;
|
|||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.WindowAdapter;
|
import java.awt.event.WindowAdapter;
|
||||||
import java.awt.event.WindowEvent;
|
import java.awt.event.WindowEvent;
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
import java.util.TreeMap;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
@@ -84,30 +82,32 @@ import net.runelite.client.ui.DynamicGridLayout;
|
|||||||
import net.runelite.client.ui.PluginPanel;
|
import net.runelite.client.ui.PluginPanel;
|
||||||
import net.runelite.client.ui.components.ComboBoxListRenderer;
|
import net.runelite.client.ui.components.ComboBoxListRenderer;
|
||||||
import net.runelite.client.ui.components.IconTextField;
|
import net.runelite.client.ui.components.IconTextField;
|
||||||
import net.runelite.client.util.SwingUtil;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ConfigPanel extends PluginPanel
|
public class ConfigPanel extends PluginPanel
|
||||||
{
|
{
|
||||||
private static final int SPINNER_FIELD_WIDTH = 6;
|
private static final int SPINNER_FIELD_WIDTH = 6;
|
||||||
|
|
||||||
private static final ImageIcon CONFIG_ICON;
|
|
||||||
private static final ImageIcon CONFIG_ICON_HOVER;
|
|
||||||
private static final ImageIcon ON_SWITCHER;
|
|
||||||
private static final ImageIcon OFF_SWITCHER;
|
|
||||||
private static final ImageIcon SEARCH;
|
private static final ImageIcon SEARCH;
|
||||||
|
|
||||||
|
private static final String RUNELITE_PLUGIN = "RuneLite";
|
||||||
|
private static final String CHAT_COLOR_PLUGIN = "Chat Color";
|
||||||
|
|
||||||
|
private final PluginManager pluginManager;
|
||||||
|
private final ConfigManager configManager;
|
||||||
|
private final ScheduledExecutorService executorService;
|
||||||
|
private final RuneLiteConfig runeLiteConfig;
|
||||||
|
private final ChatColorConfig chatColorConfig;
|
||||||
|
private final IconTextField searchBar = new IconTextField();
|
||||||
|
private final List<PluginListItem> pluginList = new ArrayList<>();
|
||||||
|
private DisplayMode currentMode = DisplayMode.PLUGIN_LIST;
|
||||||
|
private int scrollBarPosition = 0;
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
synchronized (ImageIO.class)
|
synchronized (ImageIO.class)
|
||||||
{
|
{
|
||||||
BufferedImage configIcon = ImageIO.read(ConfigPanel.class.getResourceAsStream("config_edit_icon.png"));
|
|
||||||
CONFIG_ICON = new ImageIcon(configIcon);
|
|
||||||
CONFIG_ICON_HOVER = new ImageIcon(SwingUtil.grayscaleOffset(configIcon, -100));
|
|
||||||
ON_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/on.png")));
|
|
||||||
OFF_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/off.png")));
|
|
||||||
SEARCH = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search.png")));
|
SEARCH = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search.png")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,16 +117,7 @@ public class ConfigPanel extends PluginPanel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final PluginManager pluginManager;
|
ConfigPanel(PluginManager pluginManager, ConfigManager configManager, ScheduledExecutorService executorService,
|
||||||
private final ConfigManager configManager;
|
|
||||||
private final ScheduledExecutorService executorService;
|
|
||||||
private final RuneLiteConfig runeLiteConfig;
|
|
||||||
private final ChatColorConfig chatColorConfig;
|
|
||||||
private final IconTextField searchBar = new IconTextField();
|
|
||||||
private Map<String, JPanel> children = new TreeMap<>();
|
|
||||||
private int scrollBarPosition = 0;
|
|
||||||
|
|
||||||
public ConfigPanel(PluginManager pluginManager, ConfigManager configManager, ScheduledExecutorService executorService,
|
|
||||||
RuneLiteConfig runeLiteConfig, ChatColorConfig chatColorConfig)
|
RuneLiteConfig runeLiteConfig, ChatColorConfig chatColorConfig)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
@@ -165,210 +156,55 @@ public class ConfigPanel extends PluginPanel
|
|||||||
setLayout(new DynamicGridLayout(0, 1, 0, 5));
|
setLayout(new DynamicGridLayout(0, 1, 0, 5));
|
||||||
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||||
|
|
||||||
rebuildPluginList();
|
initializePluginList();
|
||||||
openConfigList();
|
refreshPluginList();
|
||||||
}
|
}
|
||||||
|
|
||||||
final void rebuildPluginList()
|
private void initializePluginList()
|
||||||
{
|
{
|
||||||
scrollBarPosition = getScrollPane().getVerticalScrollBar().getValue();
|
// populate pluginList with all non-hidden plugins
|
||||||
Map<String, JPanel> newChildren = new TreeMap<>();
|
|
||||||
|
|
||||||
pluginManager.getPlugins().stream()
|
pluginManager.getPlugins().stream()
|
||||||
.filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden())
|
.filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden())
|
||||||
.sorted(Comparator.comparing(left -> left.getClass().getAnnotation(PluginDescriptor.class).name()))
|
|
||||||
.forEach(plugin ->
|
.forEach(plugin ->
|
||||||
{
|
{
|
||||||
final Config pluginConfigProxy = pluginManager.getPluginConfigProxy(plugin);
|
final Config config = pluginManager.getPluginConfigProxy(plugin);
|
||||||
final String pluginName = plugin.getClass().getAnnotation(PluginDescriptor.class).name();
|
final ConfigDescriptor configDescriptor = config == null ? null : configManager.getConfigDescriptor(config);
|
||||||
|
|
||||||
final JPanel groupPanel = buildGroupPanel();
|
pluginList.add(new PluginListItem(this, plugin, config, configDescriptor));
|
||||||
|
|
||||||
JLabel name = new JLabel(pluginName);
|
|
||||||
name.setForeground(Color.WHITE);
|
|
||||||
|
|
||||||
groupPanel.add(name, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
final JPanel buttonPanel = new JPanel();
|
|
||||||
buttonPanel.setLayout(new GridLayout(1, 2));
|
|
||||||
groupPanel.add(buttonPanel, BorderLayout.LINE_END);
|
|
||||||
|
|
||||||
final JLabel editConfigButton = buildConfigButton(pluginConfigProxy);
|
|
||||||
buttonPanel.add(editConfigButton);
|
|
||||||
|
|
||||||
final JLabel toggleButton = buildToggleButton(plugin);
|
|
||||||
toggleButton.setHorizontalAlignment(SwingConstants.RIGHT);
|
|
||||||
buttonPanel.add(toggleButton);
|
|
||||||
|
|
||||||
newChildren.put(pluginName, groupPanel);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
addCoreConfig(newChildren, "RuneLite", runeLiteConfig);
|
// add special entries for core client configurations
|
||||||
addCoreConfig(newChildren, "Chat Color", chatColorConfig);
|
pluginList.add(new PluginListItem(this, runeLiteConfig, configManager.getConfigDescriptor(runeLiteConfig),
|
||||||
|
RUNELITE_PLUGIN, "RuneLite client settings", "client"));
|
||||||
|
pluginList.add(new PluginListItem(this, chatColorConfig, configManager.getConfigDescriptor(chatColorConfig),
|
||||||
|
CHAT_COLOR_PLUGIN, "Recolor chat text", "colour", "messages"));
|
||||||
|
|
||||||
children = newChildren;
|
pluginList.sort(Comparator.comparing(PluginListItem::getName));
|
||||||
openConfigList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCoreConfig(Map<String, JPanel> newChildren, String configName, Config config)
|
void refreshPluginList()
|
||||||
{
|
{
|
||||||
final JPanel groupPanel = buildGroupPanel();
|
scrollBarPosition = getScrollPane().getVerticalScrollBar().getValue();
|
||||||
|
|
||||||
JLabel name = new JLabel(configName);
|
// update enabled / disabled status of all items
|
||||||
name.setForeground(Color.WHITE);
|
pluginList.forEach(listItem ->
|
||||||
|
|
||||||
groupPanel.add(name, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
final JPanel buttonPanel = new JPanel();
|
|
||||||
buttonPanel.setLayout(new GridLayout(1, 2));
|
|
||||||
groupPanel.add(buttonPanel, BorderLayout.LINE_END);
|
|
||||||
|
|
||||||
final JLabel editConfigButton = buildConfigButton(config);
|
|
||||||
buttonPanel.add(editConfigButton);
|
|
||||||
|
|
||||||
final JLabel toggleButton = buildToggleButton(null);
|
|
||||||
toggleButton.setVisible(false);
|
|
||||||
buttonPanel.add(toggleButton);
|
|
||||||
|
|
||||||
newChildren.put(configName, groupPanel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private JPanel buildGroupPanel()
|
|
||||||
{
|
|
||||||
// Create base panel for the config button and enabled/disabled button
|
|
||||||
final JPanel groupPanel = new JPanel();
|
|
||||||
groupPanel.setLayout(new BorderLayout(3, 0));
|
|
||||||
groupPanel.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH, 20));
|
|
||||||
return groupPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JLabel buildConfigButton(Config config)
|
|
||||||
{
|
|
||||||
// Create edit config button and disable it by default
|
|
||||||
final JLabel editConfigButton = new JLabel(CONFIG_ICON);
|
|
||||||
editConfigButton.setPreferredSize(new Dimension(25, 0));
|
|
||||||
editConfigButton.setVisible(false);
|
|
||||||
|
|
||||||
// If we have configuration proxy enable the button and add edit config listener
|
|
||||||
if (config != null)
|
|
||||||
{
|
{
|
||||||
final ConfigDescriptor configDescriptor = configManager.getConfigDescriptor(config);
|
final Plugin plugin = listItem.getPlugin();
|
||||||
final boolean configEmpty = configDescriptor.getItems().stream().allMatch(item -> item.getItem().hidden());
|
if (plugin != null)
|
||||||
|
|
||||||
if (!configEmpty)
|
|
||||||
{
|
{
|
||||||
editConfigButton.addMouseListener(new MouseAdapter()
|
listItem.setPluginEnabled(pluginManager.isPluginEnabled(plugin));
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mousePressed(MouseEvent mouseEvent)
|
|
||||||
{
|
|
||||||
editConfigButton.setIcon(CONFIG_ICON);
|
|
||||||
openGroupConfigPanel(config, configDescriptor, configManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseEntered(MouseEvent e)
|
|
||||||
{
|
|
||||||
editConfigButton.setIcon(CONFIG_ICON_HOVER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseExited(MouseEvent e)
|
|
||||||
{
|
|
||||||
editConfigButton.setIcon(CONFIG_ICON);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
editConfigButton.setVisible(true);
|
|
||||||
editConfigButton.setToolTipText("Edit plugin configuration");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return editConfigButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JLabel buildToggleButton(Plugin plugin)
|
|
||||||
{
|
|
||||||
// Create enabling/disabling button
|
|
||||||
final JLabel toggleButton = new JLabel(ON_SWITCHER);
|
|
||||||
toggleButton.setPreferredSize(new Dimension(25, 0));
|
|
||||||
|
|
||||||
if (plugin == null)
|
|
||||||
{
|
|
||||||
toggleButton.setEnabled(false);
|
|
||||||
return toggleButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
highlightButton(toggleButton, pluginManager.isPluginEnabled(plugin));
|
|
||||||
|
|
||||||
toggleButton.addMouseListener(new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mousePressed(MouseEvent mouseEvent)
|
|
||||||
{
|
|
||||||
executorService.submit(() ->
|
|
||||||
{
|
|
||||||
final boolean enabled = pluginManager.isPluginEnabled(plugin);
|
|
||||||
pluginManager.setPluginEnabled(plugin, !enabled);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (enabled)
|
|
||||||
{
|
|
||||||
pluginManager.stopPlugin(plugin);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pluginManager.startPlugin(plugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (PluginInstantiationException ex)
|
|
||||||
{
|
|
||||||
log.warn("Error during starting/stopping plugin {}", plugin.getClass().getSimpleName(), ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
highlightButton(toggleButton, !enabled);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return toggleButton;
|
if (currentMode == DisplayMode.PLUGIN_LIST)
|
||||||
}
|
|
||||||
|
|
||||||
private void highlightButton(JLabel button, boolean enabled)
|
|
||||||
{
|
|
||||||
button.setIcon(enabled ? ON_SWITCHER : OFF_SWITCHER);
|
|
||||||
button.setToolTipText(enabled ? "Disable plugin" : "Enable plugin");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onSearchBarChanged()
|
|
||||||
{
|
|
||||||
final String text = searchBar.getText();
|
|
||||||
|
|
||||||
children.values().forEach(this::remove);
|
|
||||||
|
|
||||||
if (text.isEmpty())
|
|
||||||
{
|
{
|
||||||
children.values().forEach(this::add);
|
openConfigList();
|
||||||
revalidate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FuzzySearch.findAndProcess(text, children.keySet(), (k) -> add(children.get(k)));
|
|
||||||
revalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivate()
|
|
||||||
{
|
|
||||||
super.onActivate();
|
|
||||||
if (searchBar.getParent() != null)
|
|
||||||
{
|
|
||||||
searchBar.requestFocusInWindow();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openConfigList()
|
private void openConfigList()
|
||||||
{
|
{
|
||||||
|
currentMode = DisplayMode.PLUGIN_LIST;
|
||||||
removeAll();
|
removeAll();
|
||||||
|
|
||||||
JLabel title = new JLabel("Configuration", SwingConstants.LEFT);
|
JLabel title = new JLabel("Configuration", SwingConstants.LEFT);
|
||||||
@@ -384,62 +220,35 @@ public class ConfigPanel extends PluginPanel
|
|||||||
scrollbar.getVerticalScrollBar().setValue(scrollBarPosition);
|
scrollbar.getVerticalScrollBar().setValue(scrollBarPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changeConfiguration(Config config, JComponent component, ConfigDescriptor cd, ConfigItemDescriptor cid)
|
private void onSearchBarChanged()
|
||||||
{
|
{
|
||||||
ConfigItem configItem = cid.getItem();
|
final String text = searchBar.getText();
|
||||||
|
|
||||||
if (!Strings.isNullOrEmpty(configItem.warning()))
|
pluginList.forEach(this::remove);
|
||||||
|
|
||||||
|
if (text.isEmpty())
|
||||||
{
|
{
|
||||||
final int result = JOptionPane.showOptionDialog(component, configItem.warning(),
|
pluginList.forEach(this::add);
|
||||||
"Are you sure?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
|
revalidate();
|
||||||
null, new String[]{"Yes", "No"}, "No");
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (result != JOptionPane.YES_OPTION)
|
// show plugins with keywords that matches all the given search terms
|
||||||
|
final String[] searchTerms = text.toLowerCase().split(" ");
|
||||||
|
pluginList.forEach(listItem ->
|
||||||
|
{
|
||||||
|
if (listItem.matchesSearchTerms(searchTerms))
|
||||||
{
|
{
|
||||||
openGroupConfigPanel(config, cd, configManager);
|
add(listItem);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if (component instanceof JCheckBox)
|
revalidate();
|
||||||
{
|
|
||||||
JCheckBox checkbox = (JCheckBox) component;
|
|
||||||
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), "" + checkbox.isSelected());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component instanceof JSpinner)
|
|
||||||
{
|
|
||||||
JSpinner spinner = (JSpinner) component;
|
|
||||||
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), "" + spinner.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component instanceof JTextArea)
|
|
||||||
{
|
|
||||||
JTextArea textField = (JTextArea) component;
|
|
||||||
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), textField.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component instanceof JColorChooser)
|
|
||||||
{
|
|
||||||
JColorChooser jColorChooser = (JColorChooser) component;
|
|
||||||
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), String.valueOf(jColorChooser.getColor().getRGB()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component instanceof JComboBox)
|
|
||||||
{
|
|
||||||
JComboBox jComboBox = (JComboBox) component;
|
|
||||||
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), ((Enum) jComboBox.getSelectedItem()).name());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component instanceof HotkeyButton)
|
|
||||||
{
|
|
||||||
HotkeyButton hotkeyButton = (HotkeyButton) component;
|
|
||||||
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), hotkeyButton.getValue());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openGroupConfigPanel(Config config, ConfigDescriptor cd, ConfigManager configManager)
|
void openGroupConfigPanel(Config config, ConfigDescriptor cd)
|
||||||
{
|
{
|
||||||
|
currentMode = DisplayMode.CONFIG_PANEL;
|
||||||
scrollBarPosition = getScrollPane().getVerticalScrollBar().getValue();
|
scrollBarPosition = getScrollPane().getVerticalScrollBar().getValue();
|
||||||
removeAll();
|
removeAll();
|
||||||
String name = cd.getGroup().name() + " Configuration";
|
String name = cd.getGroup().name() + " Configuration";
|
||||||
@@ -647,7 +456,7 @@ public class ConfigPanel extends PluginPanel
|
|||||||
configManager.setDefaultConfiguration(config, true);
|
configManager.setDefaultConfiguration(config, true);
|
||||||
|
|
||||||
// Reload configuration panel
|
// Reload configuration panel
|
||||||
openGroupConfigPanel(config, cd, configManager);
|
openGroupConfigPanel(config, cd);
|
||||||
});
|
});
|
||||||
add(resetButton);
|
add(resetButton);
|
||||||
|
|
||||||
@@ -658,4 +467,112 @@ public class ConfigPanel extends PluginPanel
|
|||||||
revalidate();
|
revalidate();
|
||||||
getScrollPane().getVerticalScrollBar().setValue(0);
|
getScrollPane().getVerticalScrollBar().setValue(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void changeConfiguration(Config config, JComponent component, ConfigDescriptor cd, ConfigItemDescriptor cid)
|
||||||
|
{
|
||||||
|
ConfigItem configItem = cid.getItem();
|
||||||
|
|
||||||
|
if (!Strings.isNullOrEmpty(configItem.warning()))
|
||||||
|
{
|
||||||
|
final int result = JOptionPane.showOptionDialog(component, configItem.warning(),
|
||||||
|
"Are you sure?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
|
||||||
|
null, new String[]{"Yes", "No"}, "No");
|
||||||
|
|
||||||
|
if (result != JOptionPane.YES_OPTION)
|
||||||
|
{
|
||||||
|
openGroupConfigPanel(config, cd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component instanceof JCheckBox)
|
||||||
|
{
|
||||||
|
JCheckBox checkbox = (JCheckBox) component;
|
||||||
|
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), "" + checkbox.isSelected());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component instanceof JSpinner)
|
||||||
|
{
|
||||||
|
JSpinner spinner = (JSpinner) component;
|
||||||
|
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), "" + spinner.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component instanceof JTextArea)
|
||||||
|
{
|
||||||
|
JTextArea textField = (JTextArea) component;
|
||||||
|
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), textField.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component instanceof JColorChooser)
|
||||||
|
{
|
||||||
|
JColorChooser jColorChooser = (JColorChooser) component;
|
||||||
|
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), String.valueOf(jColorChooser.getColor().getRGB()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component instanceof JComboBox)
|
||||||
|
{
|
||||||
|
JComboBox jComboBox = (JComboBox) component;
|
||||||
|
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), ((Enum) jComboBox.getSelectedItem()).name());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component instanceof HotkeyButton)
|
||||||
|
{
|
||||||
|
HotkeyButton hotkeyButton = (HotkeyButton) component;
|
||||||
|
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), hotkeyButton.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startPlugin(Plugin plugin, PluginListItem listItem)
|
||||||
|
{
|
||||||
|
executorService.submit(() ->
|
||||||
|
{
|
||||||
|
pluginManager.setPluginEnabled(plugin, true);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pluginManager.startPlugin(plugin);
|
||||||
|
}
|
||||||
|
catch (PluginInstantiationException ex)
|
||||||
|
{
|
||||||
|
log.warn("Error when starting plugin {}", plugin.getClass().getSimpleName(), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
listItem.setPluginEnabled(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopPlugin(Plugin plugin, PluginListItem listItem)
|
||||||
|
{
|
||||||
|
executorService.submit(() ->
|
||||||
|
{
|
||||||
|
pluginManager.setPluginEnabled(plugin, false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pluginManager.stopPlugin(plugin);
|
||||||
|
}
|
||||||
|
catch (PluginInstantiationException ex)
|
||||||
|
{
|
||||||
|
log.warn("Error when stopping plugin {}", plugin.getClass().getSimpleName(), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
listItem.setPluginEnabled(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivate()
|
||||||
|
{
|
||||||
|
super.onActivate();
|
||||||
|
if (searchBar.getParent() != null)
|
||||||
|
{
|
||||||
|
searchBar.requestFocusInWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DisplayMode
|
||||||
|
{
|
||||||
|
PLUGIN_LIST,
|
||||||
|
CONFIG_PANEL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -98,6 +98,6 @@ public class ConfigPlugin extends Plugin
|
|||||||
@Subscribe
|
@Subscribe
|
||||||
public void onPluginChanged(PluginChanged event)
|
public void onPluginChanged(PluginChanged event)
|
||||||
{
|
{
|
||||||
SwingUtilities.invokeLater(configPanel::rebuildPluginList);
|
SwingUtilities.invokeLater(configPanel::refreshPluginList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,131 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
* list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
package net.runelite.client.plugins.config;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import lombok.Value;
|
|
||||||
import org.apache.commons.text.similarity.JaroWinklerDistance;
|
|
||||||
|
|
||||||
public class FuzzySearch
|
|
||||||
{
|
|
||||||
private static final JaroWinklerDistance FUZZY_SCORE = new JaroWinklerDistance();
|
|
||||||
private static final double STRING_OCCURRENCE_MULTIPLIER = 3d;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to find a match and call callback on each match, sorted by score and filtered by average
|
|
||||||
*
|
|
||||||
* @param query query to search for
|
|
||||||
* @param entries entries to search in
|
|
||||||
* @param callback callback to call
|
|
||||||
*/
|
|
||||||
public static void findAndProcess(final String query, final Collection<String> entries, final Consumer<String> callback)
|
|
||||||
{
|
|
||||||
// Calculate score for each entry matching query
|
|
||||||
final Set<FuzzyMatch> matches = entries.stream()
|
|
||||||
.map(entry -> new FuzzyMatch(
|
|
||||||
FUZZY_SCORE.apply(query, entry)
|
|
||||||
+ calculateStringOccurrenceBonus(entry, query)
|
|
||||||
* STRING_OCCURRENCE_MULTIPLIER,
|
|
||||||
entry))
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
|
|
||||||
// Calculate average score of the matches to filter out the less relevant ones
|
|
||||||
final double average = matches.stream().mapToDouble(m -> m.score).average().orElse(0);
|
|
||||||
final double max = matches.stream().mapToDouble(m -> m.score).max().orElse(0);
|
|
||||||
final double limit = Math.min(average * 1.7, max);
|
|
||||||
|
|
||||||
matches.stream()
|
|
||||||
.filter(m -> m.score >= limit)
|
|
||||||
.sorted((left, right) -> Double.compare(right.score, left.score))
|
|
||||||
.map(m -> m.value)
|
|
||||||
.forEach(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates string occurrence bonus of query in the entry string
|
|
||||||
* @param entry entry string
|
|
||||||
* @param query query string
|
|
||||||
* @return string occurrence bonus
|
|
||||||
*/
|
|
||||||
private static double calculateStringOccurrenceBonus(final String entry, final String query)
|
|
||||||
{
|
|
||||||
// Exit early, no occurrence bonus for too long query
|
|
||||||
if (query.length() > entry.length())
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create relaxed variants of the input (e.g lower cased ones)
|
|
||||||
final String relaxedEntry = entry.toLowerCase();
|
|
||||||
final String relaxedQuery = query.toLowerCase();
|
|
||||||
|
|
||||||
// Create base bonus
|
|
||||||
final double base = 1d / 6d;
|
|
||||||
|
|
||||||
if (entry.equals(query))
|
|
||||||
{
|
|
||||||
return base * 6d;
|
|
||||||
}
|
|
||||||
if (entry.equals(relaxedQuery) || relaxedQuery.equals(entry))
|
|
||||||
{
|
|
||||||
return base * 5d;
|
|
||||||
}
|
|
||||||
if (relaxedEntry.equals(relaxedQuery))
|
|
||||||
{
|
|
||||||
return base * 4d;
|
|
||||||
}
|
|
||||||
if (entry.contains(query))
|
|
||||||
{
|
|
||||||
return base * 3d;
|
|
||||||
}
|
|
||||||
if (entry.contains(relaxedQuery) || relaxedEntry.contains(query))
|
|
||||||
{
|
|
||||||
return base * 2d;
|
|
||||||
}
|
|
||||||
if (relaxedEntry.contains(relaxedQuery))
|
|
||||||
{
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Value
|
|
||||||
private static class FuzzyMatch
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Score of the match
|
|
||||||
*/
|
|
||||||
double score;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Match value
|
|
||||||
*/
|
|
||||||
String value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package net.runelite.client.plugins.config;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.GridLayout;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.runelite.client.config.Config;
|
||||||
|
import net.runelite.client.config.ConfigDescriptor;
|
||||||
|
import net.runelite.client.plugins.Plugin;
|
||||||
|
import net.runelite.client.plugins.PluginDescriptor;
|
||||||
|
import net.runelite.client.ui.PluginPanel;
|
||||||
|
import net.runelite.client.util.SwingUtil;
|
||||||
|
import org.apache.commons.text.similarity.JaroWinklerDistance;
|
||||||
|
|
||||||
|
class PluginListItem extends JPanel
|
||||||
|
{
|
||||||
|
private static final JaroWinklerDistance DISTANCE = new JaroWinklerDistance();
|
||||||
|
|
||||||
|
private static final ImageIcon CONFIG_ICON;
|
||||||
|
private static final ImageIcon CONFIG_ICON_HOVER;
|
||||||
|
private static final ImageIcon ON_SWITCHER;
|
||||||
|
private static final ImageIcon OFF_SWITCHER;
|
||||||
|
|
||||||
|
private final ConfigPanel configPanel;
|
||||||
|
private @Getter @Nullable final Plugin plugin;
|
||||||
|
private @Nullable final Config config;
|
||||||
|
private @Nullable final ConfigDescriptor configDescriptor;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final String description;
|
||||||
|
private final List<String> keywords = new ArrayList<>();
|
||||||
|
|
||||||
|
private final JLabel configButton = new JLabel(CONFIG_ICON);
|
||||||
|
private final JLabel toggleButton = new JLabel(OFF_SWITCHER);
|
||||||
|
|
||||||
|
private boolean isPluginEnabled = false;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
synchronized (ImageIO.class)
|
||||||
|
{
|
||||||
|
BufferedImage configIcon = ImageIO.read(ConfigPanel.class.getResourceAsStream("config_edit_icon.png"));
|
||||||
|
CONFIG_ICON = new ImageIcon(configIcon);
|
||||||
|
CONFIG_ICON_HOVER = new ImageIcon(SwingUtil.grayscaleOffset(configIcon, -100));
|
||||||
|
ON_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/on.png")));
|
||||||
|
OFF_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/off.png")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code PluginListItem} for a plugin.
|
||||||
|
* <p>
|
||||||
|
* Note that {@code config} and {@code configDescriptor} can be {@code null}
|
||||||
|
* if there is no configuration associated with the plugin.
|
||||||
|
*/
|
||||||
|
PluginListItem(ConfigPanel configPanel, Plugin plugin, @Nullable Config config,
|
||||||
|
@Nullable ConfigDescriptor configDescriptor)
|
||||||
|
{
|
||||||
|
final PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class);
|
||||||
|
|
||||||
|
this.configPanel = configPanel;
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.config = config;
|
||||||
|
this.configDescriptor = configDescriptor;
|
||||||
|
this.name = descriptor.name();
|
||||||
|
this.description = descriptor.description();
|
||||||
|
Collections.addAll(keywords, name.toLowerCase().split(" "));
|
||||||
|
Collections.addAll(keywords, descriptor.tags());
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code PluginListItem} for a core configuration.
|
||||||
|
*/
|
||||||
|
PluginListItem(ConfigPanel configPanel, Config config, ConfigDescriptor configDescriptor,
|
||||||
|
String name, String description, String... tags)
|
||||||
|
{
|
||||||
|
this.configPanel = configPanel;
|
||||||
|
this.plugin = null;
|
||||||
|
this.config = config;
|
||||||
|
this.configDescriptor = configDescriptor;
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
Collections.addAll(keywords, name.toLowerCase().split(" "));
|
||||||
|
Collections.addAll(keywords, tags);
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialize()
|
||||||
|
{
|
||||||
|
setLayout(new BorderLayout(3, 0));
|
||||||
|
setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH, 20));
|
||||||
|
|
||||||
|
JLabel nameLabel = new JLabel(name);
|
||||||
|
nameLabel.setForeground(Color.WHITE);
|
||||||
|
|
||||||
|
if (!description.isEmpty())
|
||||||
|
{
|
||||||
|
nameLabel.setToolTipText("<html>" + name + ":<br>" + description + "</html>");
|
||||||
|
}
|
||||||
|
|
||||||
|
add(nameLabel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
final JPanel buttonPanel = new JPanel();
|
||||||
|
buttonPanel.setLayout(new GridLayout(1, 2));
|
||||||
|
add(buttonPanel, BorderLayout.LINE_END);
|
||||||
|
|
||||||
|
configButton.setPreferredSize(new Dimension(25, 0));
|
||||||
|
configButton.setVisible(false);
|
||||||
|
attachConfigButtonListener();
|
||||||
|
buttonPanel.add(configButton);
|
||||||
|
|
||||||
|
toggleButton.setPreferredSize(new Dimension(25, 0));
|
||||||
|
toggleButton.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||||
|
attachToggleButtonListener();
|
||||||
|
buttonPanel.add(toggleButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attachConfigButtonListener()
|
||||||
|
{
|
||||||
|
// no need for a listener if there are no config item to show
|
||||||
|
if (config == null || configDescriptor.getItems().stream().allMatch(item -> item.getItem().hidden()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
configButton.addMouseListener(new MouseAdapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
configButton.setIcon(CONFIG_ICON);
|
||||||
|
configPanel.openGroupConfigPanel(config, configDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered(MouseEvent e)
|
||||||
|
{
|
||||||
|
configButton.setIcon(CONFIG_ICON_HOVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited(MouseEvent e)
|
||||||
|
{
|
||||||
|
configButton.setIcon(CONFIG_ICON);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
configButton.setVisible(true);
|
||||||
|
configButton.setToolTipText("Edit plugin configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attachToggleButtonListener()
|
||||||
|
{
|
||||||
|
// no need for a listener if there is no plugin to enable / disable
|
||||||
|
if (plugin == null)
|
||||||
|
{
|
||||||
|
toggleButton.setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleButton.addMouseListener(new MouseAdapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
if (isPluginEnabled)
|
||||||
|
{
|
||||||
|
configPanel.stopPlugin(plugin, PluginListItem.this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
configPanel.startPlugin(plugin, PluginListItem.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPluginEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
isPluginEnabled = enabled;
|
||||||
|
toggleButton.setIcon(enabled ? ON_SWITCHER : OFF_SWITCHER);
|
||||||
|
toggleButton.setToolTipText(enabled ? "Disable plugin" : "Enable plugin");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if all the search terms in the given list matches at least one keyword.
|
||||||
|
* @return true if all search terms matches at least one keyword, or false if otherwise.
|
||||||
|
*/
|
||||||
|
boolean matchesSearchTerms(String[] searchTerms)
|
||||||
|
{
|
||||||
|
for (String term : searchTerms)
|
||||||
|
{
|
||||||
|
if (keywords.stream().noneMatch((t) -> t.contains(term) ||
|
||||||
|
DISTANCE.apply(t, term) > 0.9))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user