config: Refactor config panel into separate panels for each logical view
This commit is contained in:
@@ -36,12 +36,7 @@ import java.awt.event.ItemEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
@@ -61,66 +56,53 @@ import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.client.config.ChatColorConfig;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigDescriptor;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.ConfigItemDescriptor;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.config.Keybind;
|
||||
import net.runelite.client.config.ModifierlessKeybind;
|
||||
import net.runelite.client.config.Range;
|
||||
import net.runelite.client.config.RuneLiteConfig;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.plugins.PluginInstantiationException;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.PluginChanged;
|
||||
import net.runelite.client.plugins.PluginManager;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.DynamicGridLayout;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.client.ui.components.ComboBoxListRenderer;
|
||||
import net.runelite.client.ui.components.IconButton;
|
||||
import net.runelite.client.ui.components.IconTextField;
|
||||
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.ImageUtil;
|
||||
import net.runelite.client.util.SwingUtil;
|
||||
import net.runelite.client.util.Text;
|
||||
|
||||
@Slf4j
|
||||
public class ConfigPanel extends PluginPanel
|
||||
class ConfigPanel extends PluginPanel
|
||||
{
|
||||
private static final int SPINNER_FIELD_WIDTH = 6;
|
||||
private static final int SCROLLBAR_WIDTH = 17;
|
||||
private static final int OFFSET = 6;
|
||||
private static final ImageIcon BACK_ICON;
|
||||
private static final ImageIcon BACK_ICON_HOVER;
|
||||
|
||||
private static final String RUNELITE_GROUP_NAME = RuneLiteConfig.class.getAnnotation(ConfigGroup.class).value();
|
||||
private static final String PINNED_PLUGINS_CONFIG_KEY = "pinnedPlugins";
|
||||
private static final String RUNELITE_PLUGIN = "RuneLite";
|
||||
private static final String CHAT_COLOR_PLUGIN = "Chat Color";
|
||||
private final FixedWidthPanel mainPanel;
|
||||
private final JLabel title;
|
||||
private final PluginToggleButton pluginToggle;
|
||||
|
||||
private final PluginManager pluginManager;
|
||||
private final ConfigManager configManager;
|
||||
private final ScheduledExecutorService executorService;
|
||||
private final RuneLiteConfig runeLiteConfig;
|
||||
private final ChatColorConfig chatColorConfig;
|
||||
private final ColorPickerManager colorPickerManager;
|
||||
private final List<PluginListItem> pluginList = new ArrayList<>();
|
||||
@Inject
|
||||
private PluginListPanel pluginList;
|
||||
|
||||
private final IconTextField searchBar = new IconTextField();
|
||||
private final JPanel topPanel;
|
||||
private final JPanel mainPanel;
|
||||
private final JScrollPane scrollPane;
|
||||
@Inject
|
||||
private ConfigManager configManager;
|
||||
|
||||
private boolean showingPluginList = true;
|
||||
private int scrollBarPosition = 0;
|
||||
@Inject
|
||||
private PluginManager pluginManager;
|
||||
|
||||
@Inject
|
||||
private ColorPickerManager colorPickerManager;
|
||||
|
||||
private PluginConfigurationDescriptor pluginConfig = null;
|
||||
|
||||
static
|
||||
{
|
||||
@@ -129,48 +111,16 @@ public class ConfigPanel extends PluginPanel
|
||||
BACK_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(backIcon, -100));
|
||||
}
|
||||
|
||||
ConfigPanel(PluginManager pluginManager, ConfigManager configManager, ScheduledExecutorService executorService,
|
||||
RuneLiteConfig runeLiteConfig, ChatColorConfig chatColorConfig, ColorPickerManager colorPickerManager)
|
||||
public ConfigPanel()
|
||||
{
|
||||
super(false);
|
||||
this.pluginManager = pluginManager;
|
||||
this.configManager = configManager;
|
||||
this.executorService = executorService;
|
||||
this.runeLiteConfig = runeLiteConfig;
|
||||
this.chatColorConfig = chatColorConfig;
|
||||
this.colorPickerManager = colorPickerManager;
|
||||
|
||||
searchBar.setIcon(IconTextField.Icon.SEARCH);
|
||||
searchBar.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30));
|
||||
searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR);
|
||||
searchBar.getDocument().addDocumentListener(new DocumentListener()
|
||||
{
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e)
|
||||
{
|
||||
onSearchBarChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e)
|
||||
{
|
||||
onSearchBarChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e)
|
||||
{
|
||||
onSearchBarChanged();
|
||||
}
|
||||
});
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
|
||||
topPanel = new JPanel();
|
||||
JPanel topPanel = new JPanel();
|
||||
topPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
topPanel.setLayout(new BorderLayout(0, OFFSET));
|
||||
topPanel.setLayout(new BorderLayout(0, BORDER_OFFSET));
|
||||
add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
mainPanel = new FixedWidthPanel();
|
||||
@@ -182,139 +132,66 @@ public class ConfigPanel extends PluginPanel
|
||||
northPanel.setLayout(new BorderLayout());
|
||||
northPanel.add(mainPanel, BorderLayout.NORTH);
|
||||
|
||||
scrollPane = new JScrollPane(northPanel);
|
||||
JScrollPane scrollPane = new JScrollPane(northPanel);
|
||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
initializePluginList();
|
||||
refreshPluginList();
|
||||
}
|
||||
|
||||
private void initializePluginList()
|
||||
{
|
||||
final List<String> pinnedPlugins = getPinnedPluginNames();
|
||||
|
||||
// populate pluginList with all non-hidden plugins
|
||||
pluginManager.getPlugins().stream()
|
||||
.filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden())
|
||||
.forEach(plugin ->
|
||||
{
|
||||
final PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class);
|
||||
final Config config = pluginManager.getPluginConfigProxy(plugin);
|
||||
final ConfigDescriptor configDescriptor = config == null ? null : configManager.getConfigDescriptor(config);
|
||||
|
||||
final PluginListItem listItem = new PluginListItem(this, plugin, descriptor, config, configDescriptor);
|
||||
listItem.setPinned(pinnedPlugins.contains(listItem.getName()));
|
||||
pluginList.add(listItem);
|
||||
});
|
||||
|
||||
// add special entries for core client configurations
|
||||
final PluginListItem runeLite = new PluginListItem(this, runeLiteConfig,
|
||||
configManager.getConfigDescriptor(runeLiteConfig),
|
||||
RUNELITE_PLUGIN, "RuneLite client settings", "client");
|
||||
runeLite.setPinned(pinnedPlugins.contains(RUNELITE_PLUGIN));
|
||||
pluginList.add(runeLite);
|
||||
|
||||
final PluginListItem chatColor = new PluginListItem(this, chatColorConfig,
|
||||
configManager.getConfigDescriptor(chatColorConfig),
|
||||
CHAT_COLOR_PLUGIN, "Recolor chat text", "colour", "messages");
|
||||
chatColor.setPinned(pinnedPlugins.contains(CHAT_COLOR_PLUGIN));
|
||||
pluginList.add(chatColor);
|
||||
|
||||
pluginList.sort(Comparator.comparing(PluginListItem::getName));
|
||||
}
|
||||
|
||||
void refreshPluginList()
|
||||
{
|
||||
// update enabled / disabled status of all items
|
||||
pluginList.forEach(listItem ->
|
||||
{
|
||||
final Plugin plugin = listItem.getPlugin();
|
||||
if (plugin != null)
|
||||
{
|
||||
listItem.setPluginEnabled(pluginManager.isPluginEnabled(plugin));
|
||||
}
|
||||
});
|
||||
|
||||
if (showingPluginList)
|
||||
{
|
||||
openConfigList();
|
||||
}
|
||||
}
|
||||
|
||||
void openConfigList()
|
||||
{
|
||||
if (showingPluginList)
|
||||
{
|
||||
scrollBarPosition = scrollPane.getVerticalScrollBar().getValue();
|
||||
}
|
||||
|
||||
showingPluginList = true;
|
||||
|
||||
topPanel.removeAll();
|
||||
mainPanel.removeAll();
|
||||
topPanel.add(searchBar, BorderLayout.CENTER);
|
||||
|
||||
onSearchBarChanged();
|
||||
searchBar.requestFocusInWindow();
|
||||
validate();
|
||||
scrollPane.getVerticalScrollBar().setValue(scrollBarPosition);
|
||||
}
|
||||
|
||||
private void onSearchBarChanged()
|
||||
{
|
||||
final String text = searchBar.getText();
|
||||
|
||||
pluginList.forEach(mainPanel::remove);
|
||||
|
||||
showMatchingPlugins(true, text);
|
||||
showMatchingPlugins(false, text);
|
||||
|
||||
revalidate();
|
||||
}
|
||||
|
||||
private void showMatchingPlugins(boolean pinned, String text)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
{
|
||||
pluginList.stream().filter(item -> pinned == item.isPinned()).forEach(mainPanel::add);
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] searchTerms = text.toLowerCase().split(" ");
|
||||
pluginList.forEach(listItem ->
|
||||
{
|
||||
if (pinned == listItem.isPinned() && listItem.matchesSearchTerms(searchTerms))
|
||||
{
|
||||
mainPanel.add(listItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void openGroupConfigPanel(PluginListItem listItem, Config config, ConfigDescriptor cd)
|
||||
{
|
||||
showingPluginList = false;
|
||||
|
||||
scrollBarPosition = scrollPane.getVerticalScrollBar().getValue();
|
||||
topPanel.removeAll();
|
||||
mainPanel.removeAll();
|
||||
|
||||
final IconButton topPanelBackButton = new IconButton(BACK_ICON, BACK_ICON_HOVER);
|
||||
JButton topPanelBackButton = new JButton(BACK_ICON);
|
||||
topPanelBackButton.setRolloverIcon(BACK_ICON_HOVER);
|
||||
SwingUtil.removeButtonDecorations(topPanelBackButton);
|
||||
topPanelBackButton.setPreferredSize(new Dimension(22, 0));
|
||||
topPanelBackButton.setBorder(new EmptyBorder(0, 0, 0, 5));
|
||||
topPanelBackButton.addActionListener(e -> openConfigList());
|
||||
topPanelBackButton.addActionListener(e -> pluginList.getMuxer().popState());
|
||||
topPanelBackButton.setToolTipText("Back");
|
||||
topPanel.add(topPanelBackButton, BorderLayout.WEST);
|
||||
|
||||
topPanel.add(listItem.getConfigToggleButton(), BorderLayout.EAST);
|
||||
|
||||
String name = listItem.getName();
|
||||
JLabel title = new JLabel(name);
|
||||
pluginToggle = new PluginToggleButton();
|
||||
topPanel.add(pluginToggle, BorderLayout.EAST);
|
||||
title = new JLabel();
|
||||
title.setForeground(Color.WHITE);
|
||||
title.setToolTipText("<html>" + name + ":<br>" + listItem.getDescription() + "</html>");
|
||||
PluginListItem.addLabelPopupMenu(title, PluginListItem.wikiLinkMenuItem(listItem.getName()));
|
||||
topPanel.add(title);
|
||||
|
||||
topPanel.add(title);
|
||||
}
|
||||
|
||||
void init(PluginConfigurationDescriptor pluginConfig)
|
||||
{
|
||||
assert this.pluginConfig == null;
|
||||
this.pluginConfig = pluginConfig;
|
||||
|
||||
String name = pluginConfig.getName();
|
||||
title.setText(name);
|
||||
title.setForeground(Color.WHITE);
|
||||
title.setToolTipText("<html>" + name + ":<br>" + pluginConfig.getDescription() + "</html>");
|
||||
PluginListItem.addLabelPopupMenu(title, pluginConfig.createSupportMenuItem());
|
||||
|
||||
if (pluginConfig.getPlugin() != null)
|
||||
{
|
||||
pluginToggle.setSelected(pluginManager.isPluginEnabled(pluginConfig.getPlugin()));
|
||||
pluginToggle.addItemListener(i ->
|
||||
{
|
||||
if (pluginToggle.isSelected())
|
||||
{
|
||||
pluginList.startPlugin(pluginConfig.getPlugin());
|
||||
}
|
||||
else
|
||||
{
|
||||
pluginList.stopPlugin(pluginConfig.getPlugin());
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
pluginToggle.setVisible(false);
|
||||
}
|
||||
|
||||
rebuild();
|
||||
}
|
||||
|
||||
private void rebuild()
|
||||
{
|
||||
mainPanel.removeAll();
|
||||
|
||||
ConfigDescriptor cd = pluginConfig.getConfigDescriptor();
|
||||
for (ConfigItemDescriptor cid : cd.getItems())
|
||||
{
|
||||
if (cid.getItem().hidden())
|
||||
@@ -325,7 +202,7 @@ public class ConfigPanel extends PluginPanel
|
||||
JPanel item = new JPanel();
|
||||
item.setLayout(new BorderLayout());
|
||||
item.setMinimumSize(new Dimension(PANEL_WIDTH, 0));
|
||||
name = cid.getItem().name();
|
||||
String name = cid.getItem().name();
|
||||
JLabel configEntryName = new JLabel(name);
|
||||
configEntryName.setForeground(Color.WHITE);
|
||||
configEntryName.setToolTipText("<html>" + name + ":<br>" + cid.getItem().description() + "</html>");
|
||||
@@ -336,7 +213,7 @@ public class ConfigPanel extends PluginPanel
|
||||
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(listItem, config, checkbox, cd, cid));
|
||||
checkbox.addActionListener(ae -> changeConfiguration(checkbox, cd, cid));
|
||||
|
||||
item.add(checkbox, BorderLayout.EAST);
|
||||
}
|
||||
@@ -361,7 +238,7 @@ public class ConfigPanel extends PluginPanel
|
||||
Component editor = spinner.getEditor();
|
||||
JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField();
|
||||
spinnerTextField.setColumns(SPINNER_FIELD_WIDTH);
|
||||
spinner.addChangeListener(ce -> changeConfiguration(listItem, config, spinner, cd, cid));
|
||||
spinner.addChangeListener(ce -> changeConfiguration(spinner, cd, cid));
|
||||
|
||||
item.add(spinner, BorderLayout.EAST);
|
||||
}
|
||||
@@ -390,7 +267,7 @@ public class ConfigPanel extends PluginPanel
|
||||
@Override
|
||||
public void focusLost(FocusEvent e)
|
||||
{
|
||||
changeConfiguration(listItem, config, textField, cd, cid);
|
||||
changeConfiguration(textField, cd, cid);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -433,7 +310,7 @@ public class ConfigPanel extends PluginPanel
|
||||
colorPickerBtn.setBackground(c);
|
||||
colorPickerBtn.setText(ColorUtil.toHexColor(c).toUpperCase());
|
||||
});
|
||||
colorPicker.setOnClose(c -> changeConfiguration(listItem, config, colorPicker, cd, cid));
|
||||
colorPicker.setOnClose(c -> changeConfiguration(colorPicker, cd, cid));
|
||||
colorPicker.setVisible(true);
|
||||
}
|
||||
});
|
||||
@@ -499,7 +376,7 @@ public class ConfigPanel extends PluginPanel
|
||||
{
|
||||
if (e.getStateChange() == ItemEvent.SELECTED)
|
||||
{
|
||||
changeConfiguration(listItem, config, box, cd, cid);
|
||||
changeConfiguration(box, cd, cid);
|
||||
box.setToolTipText(Text.titleCase((Enum) box.getSelectedItem()));
|
||||
}
|
||||
});
|
||||
@@ -519,7 +396,7 @@ public class ConfigPanel extends PluginPanel
|
||||
@Override
|
||||
public void focusLost(FocusEvent e)
|
||||
{
|
||||
changeConfiguration(listItem, config, button, cd, cid);
|
||||
changeConfiguration(button, cd, cid);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -538,23 +415,21 @@ public class ConfigPanel extends PluginPanel
|
||||
|
||||
if (result == JOptionPane.YES_OPTION)
|
||||
{
|
||||
configManager.setDefaultConfiguration(config, true);
|
||||
configManager.setDefaultConfiguration(pluginConfig.getConfig(), true);
|
||||
|
||||
// Reload configuration panel
|
||||
openGroupConfigPanel(listItem, config, cd);
|
||||
rebuild();
|
||||
}
|
||||
});
|
||||
mainPanel.add(resetButton);
|
||||
|
||||
JButton backButton = new JButton("Back");
|
||||
backButton.addActionListener(e -> openConfigList());
|
||||
backButton.addActionListener(e -> pluginList.getMuxer().popState());
|
||||
mainPanel.add(backButton);
|
||||
|
||||
revalidate();
|
||||
scrollPane.getVerticalScrollBar().setValue(0);
|
||||
}
|
||||
|
||||
private void changeConfiguration(PluginListItem listItem, Config config, Component component, ConfigDescriptor cd, ConfigItemDescriptor cid)
|
||||
private void changeConfiguration(Component component, ConfigDescriptor cd, ConfigItemDescriptor cid)
|
||||
{
|
||||
final ConfigItem configItem = cid.getItem();
|
||||
|
||||
@@ -566,7 +441,7 @@ public class ConfigPanel extends PluginPanel
|
||||
|
||||
if (result != JOptionPane.YES_OPTION)
|
||||
{
|
||||
openGroupConfigPanel(listItem, config, cd);
|
||||
rebuild();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -603,102 +478,21 @@ public class ConfigPanel extends PluginPanel
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
private List<String> getPinnedPluginNames()
|
||||
{
|
||||
final String config = configManager.getConfiguration(RUNELITE_GROUP_NAME, PINNED_PLUGINS_CONFIG_KEY);
|
||||
|
||||
if (config == null)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Text.fromCSV(config);
|
||||
}
|
||||
|
||||
void savePinnedPlugins()
|
||||
{
|
||||
final String value = pluginList.stream()
|
||||
.filter(PluginListItem::isPinned)
|
||||
.map(PluginListItem::getName)
|
||||
.collect(Collectors.joining(","));
|
||||
|
||||
configManager.setConfiguration(RUNELITE_GROUP_NAME, PINNED_PLUGINS_CONFIG_KEY, value);
|
||||
}
|
||||
|
||||
void openConfigurationPanel(String configGroup)
|
||||
{
|
||||
for (PluginListItem pluginListItem : pluginList)
|
||||
{
|
||||
if (pluginListItem.getName().equals(configGroup))
|
||||
{
|
||||
openGroupConfigPanel(pluginListItem, pluginListItem.getConfig(), pluginListItem.getConfigDescriptor());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivate()
|
||||
{
|
||||
super.onActivate();
|
||||
|
||||
if (searchBar.getParent() != null)
|
||||
{
|
||||
searchBar.requestFocusInWindow();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize()
|
||||
{
|
||||
return new Dimension(PANEL_WIDTH + SCROLLBAR_WIDTH, super.getPreferredSize().height);
|
||||
}
|
||||
|
||||
private class FixedWidthPanel extends JPanel
|
||||
@Subscribe
|
||||
public void onPluginChanged(PluginChanged event)
|
||||
{
|
||||
@Override
|
||||
public Dimension getPreferredSize()
|
||||
if (event.getPlugin() == this.pluginConfig.getPlugin())
|
||||
{
|
||||
return new Dimension(PANEL_WIDTH, super.getPreferredSize().height);
|
||||
SwingUtilities.invokeLater(() ->
|
||||
{
|
||||
pluginToggle.setSelected(event.isLoaded());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
package net.runelite.client.plugins.config;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.swing.SwingUtilities;
|
||||
import net.runelite.api.MenuAction;
|
||||
import net.runelite.client.config.ChatColorConfig;
|
||||
@@ -34,13 +34,10 @@ import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.config.RuneLiteConfig;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.OverlayMenuClicked;
|
||||
import net.runelite.client.events.PluginChanged;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.plugins.PluginManager;
|
||||
import net.runelite.client.ui.ClientToolbar;
|
||||
import net.runelite.client.ui.NavigationButton;
|
||||
import net.runelite.client.ui.components.colorpicker.ColorPickerManager;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayMenuEntry;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
@@ -55,31 +52,35 @@ public class ConfigPlugin extends Plugin
|
||||
@Inject
|
||||
private ClientToolbar clientToolbar;
|
||||
|
||||
@Inject
|
||||
private Provider<PluginListPanel> pluginListPanelProvider;
|
||||
|
||||
@Inject
|
||||
private ConfigManager configManager;
|
||||
|
||||
@Inject
|
||||
private PluginManager pluginManager;
|
||||
|
||||
@Inject
|
||||
private ScheduledExecutorService executorService;
|
||||
|
||||
@Inject
|
||||
private RuneLiteConfig runeLiteConfig;
|
||||
|
||||
@Inject
|
||||
private ChatColorConfig chatColorConfig;
|
||||
|
||||
@Inject
|
||||
private ColorPickerManager colorPickerManager;
|
||||
private PluginListPanel pluginListPanel;
|
||||
|
||||
private ConfigPanel configPanel;
|
||||
private NavigationButton navButton;
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
configPanel = new ConfigPanel(pluginManager, configManager, executorService, runeLiteConfig, chatColorConfig, colorPickerManager);
|
||||
pluginListPanel = pluginListPanelProvider.get();
|
||||
pluginListPanel.addFakePlugin(new PluginConfigurationDescriptor(
|
||||
"RuneLite", "RuneLite client settings", new String[]{"client"},
|
||||
null, runeLiteConfig, configManager.getConfigDescriptor(runeLiteConfig)
|
||||
),
|
||||
new PluginConfigurationDescriptor(
|
||||
"Chat Color", "Recolor chat text", new String[]{"colour", "messages"},
|
||||
null, chatColorConfig, configManager.getConfigDescriptor(chatColorConfig)
|
||||
));
|
||||
pluginListPanel.rebuildPluginList();
|
||||
|
||||
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "config_icon.png");
|
||||
|
||||
@@ -87,7 +88,7 @@ public class ConfigPlugin extends Plugin
|
||||
.tooltip("Configuration")
|
||||
.icon(icon)
|
||||
.priority(0)
|
||||
.panel(configPanel)
|
||||
.panel(pluginListPanel.getMuxer())
|
||||
.build();
|
||||
|
||||
clientToolbar.addNavigation(navButton);
|
||||
@@ -99,12 +100,6 @@ public class ConfigPlugin extends Plugin
|
||||
clientToolbar.removeNavigation(navButton);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPluginChanged(PluginChanged event)
|
||||
{
|
||||
SwingUtilities.invokeLater(configPanel::refreshPluginList);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onOverlayMenuClicked(OverlayMenuClicked overlayMenuClicked)
|
||||
{
|
||||
@@ -126,7 +121,7 @@ public class ConfigPlugin extends Plugin
|
||||
{
|
||||
navButton.getOnSelect().run();
|
||||
}
|
||||
configPanel.openConfigurationPanel(descriptor.name());
|
||||
pluginListPanel.openConfigurationPanel(descriptor.name());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* 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.Dimension;
|
||||
import javax.swing.JPanel;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
|
||||
class FixedWidthPanel extends JPanel
|
||||
{
|
||||
@Override
|
||||
public Dimension getPreferredSize()
|
||||
{
|
||||
return new Dimension(PluginPanel.PANEL_WIDTH, super.getPreferredSize().height);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,7 +31,7 @@ import lombok.Getter;
|
||||
import net.runelite.client.config.Keybind;
|
||||
import net.runelite.client.config.ModifierlessKeybind;
|
||||
|
||||
public class HotkeyButton extends JButton
|
||||
class HotkeyButton extends JButton
|
||||
{
|
||||
@Getter
|
||||
private Keybind value;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Abex
|
||||
* 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 javax.annotation.Nullable;
|
||||
import javax.swing.JMenuItem;
|
||||
import lombok.Value;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigDescriptor;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.util.LinkBrowser;
|
||||
|
||||
@Value
|
||||
class PluginConfigurationDescriptor
|
||||
{
|
||||
private final String name;
|
||||
private final String description;
|
||||
private final String[] tags;
|
||||
|
||||
// Can be null if its not an actual plugin (RuneLite / ChatColors)
|
||||
@Nullable
|
||||
private final Plugin plugin;
|
||||
|
||||
// Can be null if it has no more configuration than the on/off toggle
|
||||
@Nullable
|
||||
private final Config config;
|
||||
|
||||
@Nullable
|
||||
private final ConfigDescriptor configDescriptor;
|
||||
|
||||
boolean hasConfigurables()
|
||||
{
|
||||
return configDescriptor != null && !configDescriptor.getItems().stream().allMatch(item -> item.getItem().hidden());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a menu item for linking to a support page for the plugin
|
||||
*
|
||||
* @return A {@link JMenuItem} which opens the plugin's wiki page URL in the browser when clicked
|
||||
*/
|
||||
JMenuItem createSupportMenuItem()
|
||||
{
|
||||
final JMenuItem menuItem = new JMenuItem("Wiki");
|
||||
menuItem.addActionListener(e -> LinkBrowser.browse("https://github.com/runelite/runelite/wiki/" + name.replace(' ', '-')));
|
||||
return menuItem;
|
||||
}
|
||||
}
|
||||
@@ -38,91 +38,49 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import lombok.AccessLevel;
|
||||
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.ColorScheme;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.client.ui.components.IconButton;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import net.runelite.client.util.LinkBrowser;
|
||||
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 String RUNELITE_WIKI_FORMAT = "https://github.com/runelite/runelite/wiki/%s";
|
||||
|
||||
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 ON_STAR;
|
||||
private static final ImageIcon OFF_STAR;
|
||||
|
||||
private final ConfigPanel configPanel;
|
||||
private final PluginListPanel pluginListPanel;
|
||||
|
||||
@Getter
|
||||
@Nullable
|
||||
private final Plugin plugin;
|
||||
|
||||
@Nullable
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private final Config config;
|
||||
|
||||
@Nullable
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private final ConfigDescriptor configDescriptor;
|
||||
|
||||
@Getter
|
||||
private final String name;
|
||||
|
||||
@Getter
|
||||
private final String description;
|
||||
|
||||
@Getter
|
||||
private final IconButton configToggleButton;
|
||||
private final PluginConfigurationDescriptor pluginConfig;
|
||||
|
||||
private final List<String> keywords = new ArrayList<>();
|
||||
|
||||
private final IconButton pinButton = new IconButton(OFF_STAR);
|
||||
private final IconButton configButton = new IconButton(CONFIG_ICON, CONFIG_ICON_HOVER);
|
||||
private final IconButton toggleButton;
|
||||
|
||||
private boolean isPluginEnabled = false;
|
||||
|
||||
@Getter
|
||||
private boolean isPinned = false;
|
||||
private final JToggleButton pinButton;
|
||||
private final JToggleButton onOffToggle;
|
||||
|
||||
static
|
||||
{
|
||||
BufferedImage configIcon = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "config_edit_icon.png");
|
||||
BufferedImage onSwitcher = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "switcher_on.png");
|
||||
BufferedImage onStar = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "star_on.png");
|
||||
CONFIG_ICON = new ImageIcon(configIcon);
|
||||
ON_SWITCHER = new ImageIcon(onSwitcher);
|
||||
ON_STAR = new ImageIcon(onStar);
|
||||
CONFIG_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(configIcon, -100));
|
||||
BufferedImage offSwitcherImage = ImageUtil.flipImage(
|
||||
ImageUtil.grayscaleOffset(
|
||||
ImageUtil.grayscaleImage(onSwitcher),
|
||||
0.61f
|
||||
),
|
||||
true,
|
||||
false
|
||||
);
|
||||
OFF_SWITCHER = new ImageIcon(offSwitcherImage);
|
||||
|
||||
BufferedImage offStar = ImageUtil.grayscaleOffset(
|
||||
ImageUtil.grayscaleImage(onStar),
|
||||
0.77f
|
||||
@@ -130,76 +88,54 @@ class PluginListItem extends JPanel
|
||||
OFF_STAR = new ImageIcon(offStar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, PluginDescriptor descriptor,
|
||||
@Nullable Config config, @Nullable ConfigDescriptor configDescriptor)
|
||||
PluginListItem(PluginListPanel pluginListPanel, PluginConfigurationDescriptor pluginConfig)
|
||||
{
|
||||
this(configPanel, plugin, config, configDescriptor,
|
||||
descriptor.name(), descriptor.description(), descriptor.tags());
|
||||
}
|
||||
this.pluginListPanel = pluginListPanel;
|
||||
this.pluginConfig = pluginConfig;
|
||||
|
||||
/**
|
||||
* Creates a new {@code PluginListItem} for a core configuration.
|
||||
*/
|
||||
PluginListItem(ConfigPanel configPanel, Config config, ConfigDescriptor configDescriptor,
|
||||
String name, String description, String... tags)
|
||||
{
|
||||
this(configPanel, null, config, configDescriptor, name, description, tags);
|
||||
}
|
||||
|
||||
private PluginListItem(ConfigPanel configPanel, @Nullable Plugin plugin, @Nullable Config config,
|
||||
@Nullable ConfigDescriptor configDescriptor, String name, String description, String... tags)
|
||||
{
|
||||
this.configPanel = configPanel;
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.configDescriptor = configDescriptor;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
Collections.addAll(keywords, name.toLowerCase().split(" "));
|
||||
Collections.addAll(keywords, description.toLowerCase().split(" "));
|
||||
Collections.addAll(keywords, tags);
|
||||
Collections.addAll(keywords, pluginConfig.getName().toLowerCase().split(" "));
|
||||
Collections.addAll(keywords, pluginConfig.getDescription().toLowerCase().split(" "));
|
||||
Collections.addAll(keywords, pluginConfig.getTags());
|
||||
|
||||
final List<JMenuItem> popupMenuItems = new ArrayList<>();
|
||||
|
||||
setLayout(new BorderLayout(3, 0));
|
||||
setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH, 20));
|
||||
|
||||
JLabel nameLabel = new JLabel(name);
|
||||
JLabel nameLabel = new JLabel(pluginConfig.getName());
|
||||
nameLabel.setForeground(Color.WHITE);
|
||||
|
||||
if (!description.isEmpty())
|
||||
if (!pluginConfig.getDescription().isEmpty())
|
||||
{
|
||||
nameLabel.setToolTipText("<html>" + name + ":<br>" + description + "</html>");
|
||||
nameLabel.setToolTipText("<html>" + pluginConfig.getName() + ":<br>" + pluginConfig.getDescription() + "</html>");
|
||||
}
|
||||
|
||||
|
||||
pinButton = new JToggleButton(OFF_STAR);
|
||||
pinButton.setSelectedIcon(ON_STAR);
|
||||
SwingUtil.removeButtonDecorations(pinButton);
|
||||
SwingUtil.addModalTooltip(pinButton, "Unpin plugin", "Pin plugin");
|
||||
pinButton.setPreferredSize(new Dimension(21, 0));
|
||||
add(pinButton, BorderLayout.LINE_START);
|
||||
|
||||
pinButton.addActionListener(e ->
|
||||
{
|
||||
setPinned(!isPinned);
|
||||
configPanel.savePinnedPlugins();
|
||||
configPanel.openConfigList();
|
||||
pluginListPanel.savePinnedPlugins();
|
||||
pluginListPanel.refresh();
|
||||
});
|
||||
|
||||
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);
|
||||
buttonPanel.add(configButton);
|
||||
|
||||
// add a listener to configButton only if there are config items to show
|
||||
if (config != null && !configDescriptor.getItems().stream().allMatch(item -> item.getItem().hidden()))
|
||||
if (pluginConfig.hasConfigurables())
|
||||
{
|
||||
JButton configButton = new JButton(CONFIG_ICON);
|
||||
configButton.setRolloverIcon(CONFIG_ICON_HOVER);
|
||||
SwingUtil.removeButtonDecorations(configButton);
|
||||
configButton.setPreferredSize(new Dimension(25, 0));
|
||||
configButton.setVisible(false);
|
||||
buttonPanel.add(configButton);
|
||||
|
||||
configButton.addActionListener(e ->
|
||||
{
|
||||
configButton.setIcon(CONFIG_ICON);
|
||||
@@ -214,72 +150,50 @@ class PluginListItem extends JPanel
|
||||
popupMenuItems.add(configMenuItem);
|
||||
}
|
||||
|
||||
popupMenuItems.add(wikiLinkMenuItem(name));
|
||||
popupMenuItems.add(pluginConfig.createSupportMenuItem());
|
||||
addLabelPopupMenu(nameLabel, popupMenuItems);
|
||||
add(nameLabel, BorderLayout.CENTER);
|
||||
|
||||
toggleButton = createToggleButton();
|
||||
buttonPanel.add(toggleButton);
|
||||
|
||||
configToggleButton = createToggleButton();
|
||||
}
|
||||
|
||||
private void attachToggleButtonListener(IconButton button)
|
||||
{
|
||||
// no need for a listener if there is no plugin to enable / disable
|
||||
if (plugin == null)
|
||||
onOffToggle = new PluginToggleButton();
|
||||
buttonPanel.add(onOffToggle);
|
||||
if (pluginConfig.getPlugin() != null)
|
||||
{
|
||||
button.setVisible(false);
|
||||
return;
|
||||
onOffToggle.addItemListener(i ->
|
||||
{
|
||||
if (onOffToggle.isSelected())
|
||||
{
|
||||
pluginListPanel.startPlugin(pluginConfig.getPlugin());
|
||||
}
|
||||
else
|
||||
{
|
||||
pluginListPanel.stopPlugin(pluginConfig.getPlugin());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
button.addActionListener(e ->
|
||||
else
|
||||
{
|
||||
if (isPluginEnabled)
|
||||
{
|
||||
configPanel.stopPlugin(plugin, PluginListItem.this);
|
||||
}
|
||||
else
|
||||
{
|
||||
configPanel.startPlugin(plugin, PluginListItem.this);
|
||||
}
|
||||
|
||||
setPluginEnabled(!isPluginEnabled);
|
||||
updateToggleButton(button);
|
||||
});
|
||||
onOffToggle.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
private IconButton createToggleButton()
|
||||
boolean isPinned()
|
||||
{
|
||||
final IconButton button = new IconButton(OFF_SWITCHER);
|
||||
button.setPreferredSize(new Dimension(25, 0));
|
||||
updateToggleButton(button);
|
||||
attachToggleButtonListener(button);
|
||||
return button;
|
||||
}
|
||||
|
||||
void setPluginEnabled(boolean enabled)
|
||||
{
|
||||
isPluginEnabled = enabled;
|
||||
updateToggleButton(toggleButton);
|
||||
updateToggleButton(configToggleButton);
|
||||
return pinButton.isSelected();
|
||||
}
|
||||
|
||||
void setPinned(boolean pinned)
|
||||
{
|
||||
isPinned = pinned;
|
||||
pinButton.setIcon(pinned ? ON_STAR : OFF_STAR);
|
||||
pinButton.setToolTipText(pinned ? "Unpin plugin" : "Pin plugin");
|
||||
pinButton.setSelected(pinned);
|
||||
}
|
||||
|
||||
private void updateToggleButton(IconButton button)
|
||||
void setPluginEnabled(boolean enabled)
|
||||
{
|
||||
button.setIcon(isPluginEnabled ? ON_SWITCHER : OFF_SWITCHER);
|
||||
button.setToolTipText(isPluginEnabled ? "Disable plugin" : "Enable plugin");
|
||||
onOffToggle.setSelected(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
@@ -297,7 +211,7 @@ class PluginListItem extends JPanel
|
||||
|
||||
private void openGroupConfigPanel()
|
||||
{
|
||||
configPanel.openGroupConfigPanel(PluginListItem.this, config, configDescriptor);
|
||||
pluginListPanel.openConfigurationPanel(pluginConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,19 +274,4 @@ class PluginListItem extends JPanel
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a menu item for linking to a wiki page which, when clicked, opens a link to the plugin's wiki page for
|
||||
* the passed plugin name.
|
||||
*
|
||||
* @param pluginName The name of the plugin which should be linked to
|
||||
* @return A {@link JMenuItem} which opens the plugin's wiki page URL in the browser when clicked
|
||||
*/
|
||||
static JMenuItem wikiLinkMenuItem(final String pluginName)
|
||||
{
|
||||
final JMenuItem menuItem = new JMenuItem("Wiki");
|
||||
final String sanitizedName = pluginName.replace(' ', '-');
|
||||
menuItem.addActionListener(e -> LinkBrowser.browse(String.format(RUNELITE_WIKI_FORMAT, sanitizedName)));
|
||||
return menuItem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* 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.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigDescriptor;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.config.RuneLiteConfig;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.PluginChanged;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.plugins.PluginInstantiationException;
|
||||
import net.runelite.client.plugins.PluginManager;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.DynamicGridLayout;
|
||||
import net.runelite.client.ui.MultiplexingPluginPanel;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.client.ui.components.IconTextField;
|
||||
import net.runelite.client.util.Text;
|
||||
|
||||
@Slf4j
|
||||
@Singleton
|
||||
class PluginListPanel extends PluginPanel
|
||||
{
|
||||
private static final String RUNELITE_GROUP_NAME = RuneLiteConfig.class.getAnnotation(ConfigGroup.class).value();
|
||||
private static final String PINNED_PLUGINS_CONFIG_KEY = "pinnedPlugins";
|
||||
|
||||
private final ConfigManager configManager;
|
||||
private final PluginManager pluginManager;
|
||||
private final ScheduledExecutorService executorService;
|
||||
private final Provider<ConfigPanel> configPanelProvider;
|
||||
private final List<PluginConfigurationDescriptor> fakePlugins = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
private final MultiplexingPluginPanel muxer;
|
||||
private final IconTextField searchBar;
|
||||
private final JScrollPane scrollPane;
|
||||
private final FixedWidthPanel mainPanel;
|
||||
private List<PluginListItem> pluginList;
|
||||
|
||||
@Inject
|
||||
public PluginListPanel(
|
||||
ConfigManager configManager,
|
||||
PluginManager pluginManager,
|
||||
ScheduledExecutorService executorService,
|
||||
EventBus eventBus,
|
||||
Provider<ConfigPanel> configPanelProvider)
|
||||
{
|
||||
super(false);
|
||||
|
||||
this.configManager = configManager;
|
||||
this.pluginManager = pluginManager;
|
||||
this.executorService = executorService;
|
||||
this.configPanelProvider = configPanelProvider;
|
||||
|
||||
muxer = new MultiplexingPluginPanel(this)
|
||||
{
|
||||
@Override
|
||||
protected void onAdd(PluginPanel p)
|
||||
{
|
||||
eventBus.register(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemove(PluginPanel p)
|
||||
{
|
||||
eventBus.unregister(p);
|
||||
}
|
||||
};
|
||||
|
||||
searchBar = new IconTextField();
|
||||
searchBar.setIcon(IconTextField.Icon.SEARCH);
|
||||
searchBar.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30));
|
||||
searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR);
|
||||
searchBar.getDocument().addDocumentListener(new DocumentListener()
|
||||
{
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e)
|
||||
{
|
||||
onSearchBarChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e)
|
||||
{
|
||||
onSearchBarChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e)
|
||||
{
|
||||
onSearchBarChanged();
|
||||
}
|
||||
});
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
|
||||
JPanel topPanel = new JPanel();
|
||||
topPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
topPanel.setLayout(new BorderLayout(0, BORDER_OFFSET));
|
||||
topPanel.add(searchBar, BorderLayout.CENTER);
|
||||
add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
mainPanel = new FixedWidthPanel();
|
||||
mainPanel.setBorder(new EmptyBorder(8, 10, 10, 10));
|
||||
mainPanel.setLayout(new DynamicGridLayout(0, 1, 0, 5));
|
||||
mainPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
|
||||
JPanel northPanel = new FixedWidthPanel();
|
||||
northPanel.setLayout(new BorderLayout());
|
||||
northPanel.add(mainPanel, BorderLayout.NORTH);
|
||||
|
||||
scrollPane = new JScrollPane(northPanel);
|
||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
void rebuildPluginList()
|
||||
{
|
||||
final List<String> pinnedPlugins = getPinnedPluginNames();
|
||||
|
||||
// populate pluginList with all non-hidden plugins
|
||||
pluginList = Stream.concat(
|
||||
fakePlugins.stream(),
|
||||
pluginManager.getPlugins().stream()
|
||||
.filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden())
|
||||
.map(plugin ->
|
||||
{
|
||||
PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class);
|
||||
Config config = pluginManager.getPluginConfigProxy(plugin);
|
||||
ConfigDescriptor configDescriptor = config == null ? null : configManager.getConfigDescriptor(config);
|
||||
|
||||
return new PluginConfigurationDescriptor(
|
||||
descriptor.name(),
|
||||
descriptor.description(),
|
||||
descriptor.tags(),
|
||||
plugin,
|
||||
config,
|
||||
configDescriptor);
|
||||
})
|
||||
).map(desc ->
|
||||
{
|
||||
PluginListItem listItem = new PluginListItem(this, desc);
|
||||
listItem.setPinned(pinnedPlugins.contains(desc.getName()));
|
||||
return listItem;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
pluginList.sort(Comparator.comparing(p -> p.getPluginConfig().getName()));
|
||||
mainPanel.removeAll();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void addFakePlugin(PluginConfigurationDescriptor... descriptor)
|
||||
{
|
||||
Collections.addAll(fakePlugins, descriptor);
|
||||
}
|
||||
|
||||
void refresh()
|
||||
{
|
||||
// update enabled / disabled status of all items
|
||||
pluginList.forEach(listItem ->
|
||||
{
|
||||
final Plugin plugin = listItem.getPluginConfig().getPlugin();
|
||||
if (plugin != null)
|
||||
{
|
||||
listItem.setPluginEnabled(pluginManager.isPluginEnabled(plugin));
|
||||
}
|
||||
});
|
||||
|
||||
int scrollBarPosition = scrollPane.getVerticalScrollBar().getValue();
|
||||
|
||||
onSearchBarChanged();
|
||||
searchBar.requestFocusInWindow();
|
||||
validate();
|
||||
|
||||
scrollPane.getVerticalScrollBar().setValue(scrollBarPosition);
|
||||
}
|
||||
|
||||
private void onSearchBarChanged()
|
||||
{
|
||||
final String text = searchBar.getText();
|
||||
|
||||
pluginList.forEach(mainPanel::remove);
|
||||
|
||||
showMatchingPlugins(true, text);
|
||||
showMatchingPlugins(false, text);
|
||||
|
||||
revalidate();
|
||||
}
|
||||
|
||||
private void showMatchingPlugins(boolean pinned, String text)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
{
|
||||
pluginList.stream().filter(item -> pinned == item.isPinned()).forEach(mainPanel::add);
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] searchTerms = text.toLowerCase().split(" ");
|
||||
pluginList.forEach(listItem ->
|
||||
{
|
||||
if (pinned == listItem.isPinned() && listItem.matchesSearchTerms(searchTerms))
|
||||
{
|
||||
mainPanel.add(listItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void openConfigurationPanel(String configGroup)
|
||||
{
|
||||
for (PluginListItem pluginListItem : pluginList)
|
||||
{
|
||||
if (pluginListItem.getPluginConfig().getName().equals(configGroup))
|
||||
{
|
||||
openConfigurationPanel(pluginListItem.getPluginConfig());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void openConfigurationPanel(PluginConfigurationDescriptor plugin)
|
||||
{
|
||||
ConfigPanel panel = configPanelProvider.get();
|
||||
panel.init(plugin);
|
||||
muxer.pushState(panel);
|
||||
}
|
||||
|
||||
void startPlugin(Plugin plugin)
|
||||
{
|
||||
executorService.submit(() ->
|
||||
{
|
||||
pluginManager.setPluginEnabled(plugin, true);
|
||||
|
||||
try
|
||||
{
|
||||
pluginManager.startPlugin(plugin);
|
||||
}
|
||||
catch (PluginInstantiationException ex)
|
||||
{
|
||||
log.warn("Error when starting plugin {}", plugin.getClass().getSimpleName(), ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void stopPlugin(Plugin plugin)
|
||||
{
|
||||
executorService.submit(() ->
|
||||
{
|
||||
pluginManager.setPluginEnabled(plugin, false);
|
||||
|
||||
try
|
||||
{
|
||||
pluginManager.stopPlugin(plugin);
|
||||
}
|
||||
catch (PluginInstantiationException ex)
|
||||
{
|
||||
log.warn("Error when stopping plugin {}", plugin.getClass().getSimpleName(), ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<String> getPinnedPluginNames()
|
||||
{
|
||||
final String config = configManager.getConfiguration(RUNELITE_GROUP_NAME, PINNED_PLUGINS_CONFIG_KEY);
|
||||
|
||||
if (config == null)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Text.fromCSV(config);
|
||||
}
|
||||
|
||||
void savePinnedPlugins()
|
||||
{
|
||||
final String value = pluginList.stream()
|
||||
.filter(PluginListItem::isPinned)
|
||||
.map(p -> p.getPluginConfig().getName())
|
||||
.collect(Collectors.joining(","));
|
||||
|
||||
configManager.setConfiguration(RUNELITE_GROUP_NAME, PINNED_PLUGINS_CONFIG_KEY, value);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPluginChanged(PluginChanged event)
|
||||
{
|
||||
SwingUtilities.invokeLater(this::refresh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize()
|
||||
{
|
||||
return new Dimension(PANEL_WIDTH + SCROLLBAR_WIDTH, super.getPreferredSize().height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivate()
|
||||
{
|
||||
super.onActivate();
|
||||
|
||||
if (searchBar.getParent() != null)
|
||||
{
|
||||
searchBar.requestFocusInWindow();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Abex
|
||||
* 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.Dimension;
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JToggleButton;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import net.runelite.client.util.SwingUtil;
|
||||
|
||||
class PluginToggleButton extends JToggleButton
|
||||
{
|
||||
private static final ImageIcon ON_SWITCHER;
|
||||
private static final ImageIcon OFF_SWITCHER;
|
||||
|
||||
static
|
||||
{
|
||||
BufferedImage onSwitcher = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "switcher_on.png");
|
||||
ON_SWITCHER = new ImageIcon(onSwitcher);
|
||||
OFF_SWITCHER = new ImageIcon(ImageUtil.flipImage(
|
||||
ImageUtil.grayscaleOffset(
|
||||
ImageUtil.grayscaleImage(onSwitcher),
|
||||
0.61f
|
||||
),
|
||||
true,
|
||||
false
|
||||
));
|
||||
}
|
||||
|
||||
public PluginToggleButton()
|
||||
{
|
||||
super(OFF_SWITCHER);
|
||||
setSelectedIcon(ON_SWITCHER);
|
||||
SwingUtil.removeButtonDecorations(this);
|
||||
setPreferredSize(new Dimension(25, 0));
|
||||
SwingUtil.addModalTooltip(this, "Disable plugin", "Enable plugin");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Abex
|
||||
* 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.ui;
|
||||
|
||||
import java.awt.CardLayout;
|
||||
|
||||
public class MultiplexingPluginPanel extends PluginPanel
|
||||
{
|
||||
private final CardLayout layout;
|
||||
private boolean active = false;
|
||||
private PluginPanel current;
|
||||
|
||||
public MultiplexingPluginPanel(PluginPanel root)
|
||||
{
|
||||
super(false);
|
||||
|
||||
layout = new CardLayout();
|
||||
setLayout(layout);
|
||||
pushState(root);
|
||||
}
|
||||
|
||||
public void destroy()
|
||||
{
|
||||
for (int i = getComponentCount() - 1; i > 0; i--)
|
||||
{
|
||||
onRemove((PluginPanel) getComponent(i));
|
||||
remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
public void pushState(PluginPanel subpanel)
|
||||
{
|
||||
int index = -1;
|
||||
for (int i = getComponentCount() - 1; i >= 0; i--)
|
||||
{
|
||||
if (getComponent(i) == subpanel)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (active)
|
||||
{
|
||||
current.onDeactivate();
|
||||
subpanel.onActivate();
|
||||
}
|
||||
current = subpanel;
|
||||
|
||||
String name = System.identityHashCode(subpanel) + "";
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
for (int i = getComponentCount() - 1; i > index; i--)
|
||||
{
|
||||
popState();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
add(subpanel, name);
|
||||
onAdd(subpanel);
|
||||
}
|
||||
|
||||
layout.show(this, name);
|
||||
revalidate();
|
||||
}
|
||||
|
||||
public void popState()
|
||||
{
|
||||
int count = getComponentCount();
|
||||
if (count <= 1)
|
||||
{
|
||||
assert false : "Cannot pop last component";
|
||||
return;
|
||||
}
|
||||
|
||||
PluginPanel subpanel = (PluginPanel) getComponent(count - 2);
|
||||
if (active)
|
||||
{
|
||||
current.onDeactivate();
|
||||
subpanel.onActivate();
|
||||
current = subpanel;
|
||||
}
|
||||
layout.show(this, System.identityHashCode(subpanel) + "");
|
||||
onRemove((PluginPanel) getComponent(count - 1));
|
||||
remove(count - 1);
|
||||
revalidate();
|
||||
}
|
||||
|
||||
protected void onAdd(PluginPanel p)
|
||||
{
|
||||
}
|
||||
|
||||
protected void onRemove(PluginPanel p)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivate()
|
||||
{
|
||||
active = true;
|
||||
current.onActivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeactivate()
|
||||
{
|
||||
active = false;
|
||||
current.onDeactivate();
|
||||
}
|
||||
}
|
||||
@@ -36,9 +36,9 @@ import lombok.Getter;
|
||||
public abstract class PluginPanel extends JPanel
|
||||
{
|
||||
public static final int PANEL_WIDTH = 225;
|
||||
private static final int SCROLLBAR_WIDTH = 17;
|
||||
private static final int OFFSET = 6;
|
||||
private static final EmptyBorder BORDER_PADDING = new EmptyBorder(OFFSET, OFFSET, OFFSET, OFFSET);
|
||||
public static final int SCROLLBAR_WIDTH = 17;
|
||||
public static final int BORDER_OFFSET = 6;
|
||||
private static final EmptyBorder BORDER_PADDING = new EmptyBorder(BORDER_OFFSET, BORDER_OFFSET, BORDER_OFFSET, BORDER_OFFSET);
|
||||
private static final Dimension OUTER_PREFERRED_SIZE = new Dimension(PluginPanel.PANEL_WIDTH + SCROLLBAR_WIDTH, 0);
|
||||
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Image;
|
||||
import java.awt.Insets;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.event.MouseAdapter;
|
||||
@@ -41,6 +42,7 @@ import java.util.concurrent.Callable;
|
||||
import java.util.function.BiConsumer;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
@@ -277,4 +279,18 @@ public class SwingUtil
|
||||
navigationButton.setOnSelect(button::doClick);
|
||||
return button;
|
||||
}
|
||||
|
||||
public static void removeButtonDecorations(AbstractButton button)
|
||||
{
|
||||
button.setBorderPainted(false);
|
||||
button.setContentAreaFilled(false);
|
||||
button.setFocusPainted(false);
|
||||
button.setMargin(new Insets(0, 0, 0, 0));
|
||||
button.setOpaque(false);
|
||||
}
|
||||
|
||||
public static void addModalTooltip(AbstractButton button, String on, String off)
|
||||
{
|
||||
button.addItemListener(l -> button.setToolTipText(button.isSelected() ? on : off));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user