Merge pull request #386 from Abextm/panel-state-2

Allow PluginPanels to keep state
This commit is contained in:
Adam
2018-01-20 09:19:05 -05:00
committed by GitHub
8 changed files with 217 additions and 107 deletions

View File

@@ -30,7 +30,7 @@ import java.util.Map;
import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetInfo;
public interface Client public interface Client extends GameEngine
{ {
List<Player> getPlayers(); List<Player> getPlayers();

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018 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.api;
import java.awt.Canvas;
public interface GameEngine
{
Canvas getCanvas();
}

View File

@@ -24,23 +24,16 @@
*/ */
package net.runelite.client.plugins.config; package net.runelite.client.plugins.config;
import static javax.swing.JOptionPane.WARNING_MESSAGE;
import static javax.swing.JOptionPane.YES_NO_OPTION;
import static javax.swing.JOptionPane.YES_OPTION;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import java.awt.event.ItemEvent; import java.awt.event.ItemEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter; 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.util.Comparator;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import javax.swing.JButton; import javax.swing.JButton;
@@ -52,12 +45,18 @@ import javax.swing.JFormattedTextField;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import static javax.swing.JOptionPane.WARNING_MESSAGE;
import static javax.swing.JOptionPane.YES_NO_OPTION;
import static javax.swing.JOptionPane.YES_OPTION;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner; import javax.swing.JSpinner;
import javax.swing.JTextField; import javax.swing.JTextField;
import javax.swing.SpinnerModel; import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel; import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.client.config.ConfigDescriptor; import net.runelite.client.config.ConfigDescriptor;
import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigItem;
@@ -72,75 +71,107 @@ public class ConfigPanel extends PluginPanel
private static final int SPINNER_FIELD_WIDTH = 6; private static final int SPINNER_FIELD_WIDTH = 6;
private final ConfigManager configManager; private final ConfigManager configManager;
private JTextField searchBar; private final JTextField searchBar = new JTextField();
private Map<String, JPanel> children = new TreeMap<>();
private int scrollBarPosition = 0;
public ConfigPanel(ConfigManager configManager) public ConfigPanel(ConfigManager configManager)
{ {
super(); super();
this.configManager = configManager; this.configManager = configManager;
populateConfig();
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();
}
});
rebuildPluginList();
openConfigList();
}
final void rebuildPluginList()
{
Map<String, JPanel> newChildren = new TreeMap<>();
configManager.getConfigProxies().stream()
.map(configManager::getConfigDescriptor)
.filter(configDescriptor -> configDescriptor.getItems().stream()
.anyMatch(cid -> !cid.getItem().hidden()))
.forEach(cd ->
{
String groupName = cd.getGroup().name();
if (children.containsKey(groupName))
{
newChildren.put(groupName, children.get(groupName));
return;
}
JPanel groupPanel = new JPanel();
groupPanel.setLayout(new BorderLayout());
JButton viewGroupItemsButton = new JButton(groupName);
viewGroupItemsButton.addActionListener(ae -> openGroupConfigPanel(cd, configManager));
groupPanel.add(viewGroupItemsButton);
newChildren.put(groupName, groupPanel);
});
children = newChildren;
openConfigList();
}
private void onSearchBarChanged()
{
children.forEach((key, value) ->
{
final String text = searchBar.getText().toLowerCase();
final String labelToSearch = key.toLowerCase();
if (text.isEmpty() || labelToSearch.contains(text))
{
add(value);
}
else
{
remove(value);
}
});
revalidate();
} }
@Override @Override
public void invalidate() public void onActivate()
{ {
super.invalidate(); super.onActivate();
if (searchBar.getParent() != null)
if (searchBar != null)
{ {
searchBar.requestFocusInWindow(); searchBar.requestFocusInWindow();
} }
} }
private void populateConfig() private void openConfigList()
{ {
removeAll(); removeAll();
add(new JLabel("Plugin Configuration", SwingConstants.CENTER)); add(new JLabel("Plugin Configuration", SwingConstants.CENTER));
searchBar = new JTextField();
add(searchBar); add(searchBar);
final Map<String, JPanel> children = new TreeMap<>();
configManager.getConfigProxies().stream() onSearchBarChanged();
.map(configManager::getConfigDescriptor) searchBar.requestFocusInWindow();
.filter(configDescriptor -> configDescriptor.getItems().stream() JScrollPane scrollbar = getScrollPane();
.anyMatch(cid -> !cid.getItem().hidden())) scrollbar.validate();
.sorted(Comparator.comparing(left -> left.getGroup().name())) scrollbar.getVerticalScrollBar().setValue(scrollBarPosition);
.forEach(cd ->
{
JPanel groupPanel = new JPanel();
groupPanel.setLayout(new BorderLayout());
JButton viewGroupItemsButton = new JButton(cd.getGroup().name());
viewGroupItemsButton.addActionListener(ae -> openGroupConfigPanel(cd, configManager));
groupPanel.add(viewGroupItemsButton);
children.put(cd.getGroup().name(), groupPanel);
add(groupPanel);
});
searchBar.addKeyListener(new KeyAdapter()
{
@Override
public void keyTyped(KeyEvent e)
{
children.forEach((key, value) ->
{
final String text = searchBar.getText().toLowerCase();
final String labelToSearch = key.toLowerCase();
if (text.isEmpty() || labelToSearch.contains(text))
{
add(value);
}
else
{
remove(value);
}
});
revalidate();
}
});
revalidate();
} }
private void changeConfiguration(JComponent component, ConfigDescriptor cd, ConfigItemDescriptor cid) private void changeConfiguration(JComponent component, ConfigDescriptor cd, ConfigItemDescriptor cid)
@@ -156,7 +187,7 @@ public class ConfigPanel extends PluginPanel
{ {
int value = JOptionPane.showOptionDialog(component, configItem.confirmationWarining(), int value = JOptionPane.showOptionDialog(component, configItem.confirmationWarining(),
"Are you sure?", YES_NO_OPTION, WARNING_MESSAGE, "Are you sure?", YES_NO_OPTION, WARNING_MESSAGE,
null, new String[] { "Yes", "No" }, "No"); null, new String[]{"Yes", "No"}, "No");
if (value != YES_OPTION) if (value != YES_OPTION)
{ {
checkbox.setSelected(originalState); checkbox.setSelected(originalState);
@@ -194,6 +225,7 @@ public class ConfigPanel extends PluginPanel
private void openGroupConfigPanel(ConfigDescriptor cd, ConfigManager configManager) private void openGroupConfigPanel(ConfigDescriptor cd, ConfigManager configManager)
{ {
scrollBarPosition = getScrollPane().getVerticalScrollBar().getValue();
removeAll(); removeAll();
String name = cd.getGroup().name() + " Configuration"; String name = cd.getGroup().name() + " Configuration";
JLabel title = new JLabel(name); JLabel title = new JLabel(name);
@@ -319,15 +351,9 @@ public class ConfigPanel extends PluginPanel
} }
JButton backButton = new JButton("Back"); JButton backButton = new JButton("Back");
backButton.addActionListener(this::getBackButtonListener); backButton.addActionListener(e -> openConfigList());
add(backButton); add(backButton);
revalidate(); revalidate();
getScrollPane().getVerticalScrollBar().setValue(0);
} }
public void getBackButtonListener(ActionEvent e)
{
populateConfig();
}
} }

View File

@@ -24,9 +24,12 @@
*/ */
package net.runelite.client.plugins.config; package net.runelite.client.plugins.config;
import com.google.common.eventbus.Subscribe;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.inject.Inject; import javax.inject.Inject;
import javax.swing.SwingUtilities;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.events.PluginChanged;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.ClientUI;
@@ -44,15 +47,18 @@ public class ConfigPlugin extends Plugin
@Inject @Inject
ConfigManager configManager; ConfigManager configManager;
private ConfigPanel configPanel;
private NavigationButton navButton; private NavigationButton navButton;
@Override @Override
protected void startUp() throws Exception protected void startUp() throws Exception
{ {
configPanel = new ConfigPanel(configManager);
navButton = new NavigationButton( navButton = new NavigationButton(
"Configuration", "Configuration",
ImageIO.read(getClass().getResourceAsStream("config_icon.png")), ImageIO.read(getClass().getResourceAsStream("config_icon.png")),
() -> new ConfigPanel(configManager)); () -> configPanel);
ui.getPluginToolbar().addNavigation(navButton); ui.getPluginToolbar().addNavigation(navButton);
} }
@@ -62,4 +68,10 @@ public class ConfigPlugin extends Plugin
{ {
ui.getPluginToolbar().removeNavigation(navButton); ui.getPluginToolbar().removeNavigation(navButton);
} }
@Subscribe
public void onPluginChanged(PluginChanged event)
{
SwingUtilities.invokeLater(configPanel::rebuildPluginList);
}
} }

View File

@@ -326,6 +326,13 @@ public class HiscorePanel extends PluginPanel
details.setText(text); details.setText(text);
} }
@Override
public void onActivate()
{
super.onActivate();
input.requestFocusInWindow();
}
private JPanel makeSkillPanel(String skillName, HiscoreSkill skill) private JPanel makeSkillPanel(String skillName, HiscoreSkill skill)
{ {
JLabel label = new JLabel(); JLabel label = new JLabel();

View File

@@ -28,6 +28,7 @@ import com.google.common.base.Strings;
import java.applet.Applet; import java.applet.Applet;
import java.awt.AWTException; import java.awt.AWTException;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Frame; import java.awt.Frame;
@@ -46,9 +47,7 @@ import javax.swing.JFrame;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JRootPane; import javax.swing.JRootPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager; import javax.swing.ToolTipManager;
import javax.swing.UIManager; import javax.swing.UIManager;
@@ -65,8 +64,7 @@ import org.pushingpixels.substance.internal.ui.SubstanceRootPaneUI;
@Slf4j @Slf4j
public class ClientUI extends JFrame public class ClientUI extends JFrame
{ {
private static final int SCROLLBAR_WIDTH = 17; private static final int PANEL_EXPANDED_WIDTH = PluginPanel.PANEL_WIDTH + PluginPanel.SCROLLBAR_WIDTH;
private static final int PANEL_EXPANDED_WIDTH = PluginPanel.PANEL_WIDTH + SCROLLBAR_WIDTH;
private static final BufferedImage ICON; private static final BufferedImage ICON;
@Getter @Getter
@@ -74,7 +72,6 @@ public class ClientUI extends JFrame
private final Applet client; private final Applet client;
private final RuneLiteProperties properties; private final RuneLiteProperties properties;
private JPanel container;
private JPanel navContainer; private JPanel navContainer;
private PluginToolbar pluginToolbar; private PluginToolbar pluginToolbar;
private PluginPanel pluginPanel; private PluginPanel pluginPanel;
@@ -158,6 +155,21 @@ public class ClientUI extends JFrame
setVisible(true); setVisible(true);
toFront(); toFront();
requestFocus();
giveClientFocus();
}
private void giveClientFocus()
{
if (client instanceof Client)
{
final Canvas c = ((Client) client).getCanvas();
c.requestFocusInWindow();
}
else
{
client.requestFocusInWindow();
}
} }
private static void setUIFont(FontUIResource f) private static void setUIFont(FontUIResource f)
@@ -211,7 +223,6 @@ public class ClientUI extends JFrame
return trayIcon; return trayIcon;
} }
@Override @Override
public void setTitle(String extra) public void setTitle(String extra)
{ {
@@ -239,14 +250,14 @@ public class ClientUI extends JFrame
} }
}); });
container = new JPanel(); final JPanel container = new JPanel();
container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS)); container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
container.add(new ClientPanel(client)); container.add(new ClientPanel(client));
navContainer = new JPanel(); navContainer = new JPanel();
navContainer.setLayout(new BorderLayout(0,0)); navContainer.setLayout(new BorderLayout(0, 0));
navContainer.setMinimumSize(new Dimension(0,0)); navContainer.setMinimumSize(new Dimension(0, 0));
navContainer.setMaximumSize(new Dimension(0,Integer.MAX_VALUE)); navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE));
container.add(navContainer); container.add(navContainer);
pluginToolbar = new PluginToolbar(this); pluginToolbar = new PluginToolbar(this);
@@ -271,18 +282,28 @@ public class ClientUI extends JFrame
pluginPanel = panel; pluginPanel = panel;
navContainer.setMinimumSize(new Dimension(PANEL_EXPANDED_WIDTH, 0)); navContainer.setMinimumSize(new Dimension(PANEL_EXPANDED_WIDTH, 0));
navContainer.setMaximumSize(new Dimension(PANEL_EXPANDED_WIDTH, Integer.MAX_VALUE)); navContainer.setMaximumSize(new Dimension(PANEL_EXPANDED_WIDTH, Integer.MAX_VALUE));
navContainer.add(wrapPanel(pluginPanel));
final JPanel wrappedPanel = panel.getWrappedPanel();
navContainer.add(wrappedPanel);
navContainer.revalidate(); navContainer.revalidate();
// panel.onActivate has to go after giveClientFocus so it can get focus if it needs.
giveClientFocus();
panel.onActivate();
wrappedPanel.repaint();
revalidateMinimumSize(); revalidateMinimumSize();
} }
void contract() void contract()
{ {
boolean wasMinimumWidth = this.getWidth() == (int) this.getMinimumSize().getWidth(); boolean wasMinimumWidth = this.getWidth() == (int) this.getMinimumSize().getWidth();
pluginPanel.onDeactivate();
navContainer.remove(0); navContainer.remove(0);
navContainer.setMinimumSize(new Dimension(0, 0)); navContainer.setMinimumSize(new Dimension(0, 0));
navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE)); navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE));
navContainer.revalidate(); navContainer.revalidate();
giveClientFocus();
revalidateMinimumSize(); revalidateMinimumSize();
if (wasMinimumWidth) if (wasMinimumWidth)
{ {
@@ -291,35 +312,12 @@ public class ClientUI extends JFrame
pluginPanel = null; pluginPanel = null;
} }
private JPanel wrapPanel(PluginPanel panel)
{
final JPanel northPanel = new JPanel();
northPanel.setLayout(new BorderLayout());
northPanel.add(panel, BorderLayout.NORTH);
final JScrollPane scrollPane = new JScrollPane(northPanel);
scrollPane.getVerticalScrollBar().setUnitIncrement(16); //Otherwise scrollspeed is really slow
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
final JPanel panelWrap = new JPanel();
// Adjust the preferred size to expand to width of scrollbar to
// to preven scrollbar overlapping over contents
panelWrap.setPreferredSize(new Dimension(
PluginPanel.PANEL_WIDTH + SCROLLBAR_WIDTH,
0));
panelWrap.setLayout(new BorderLayout());
panelWrap.add(scrollPane, BorderLayout.CENTER);
return panelWrap;
}
private void checkExit() private void checkExit()
{ {
int result = JOptionPane.OK_OPTION; int result = JOptionPane.OK_OPTION;
// only ask if not logged out // only ask if not logged out
if (client != null && client instanceof Client && ((Client)client).getGameState() != GameState.LOGIN_SCREEN) if (client != null && client instanceof Client && ((Client) client).getGameState() != GameState.LOGIN_SCREEN)
{ {
result = JOptionPane.showConfirmDialog(this, "Are you sure you want to exit?", "Exit", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); result = JOptionPane.showConfirmDialog(this, "Are you sure you want to exit?", "Exit", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
} }

View File

@@ -24,22 +24,50 @@
*/ */
package net.runelite.client.ui; package net.runelite.client.ui;
import java.awt.BorderLayout;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.GridLayout; import java.awt.GridLayout;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import lombok.AccessLevel;
import lombok.Getter;
public abstract class PluginPanel extends JPanel public abstract class PluginPanel extends JPanel
{ {
public static final int PANEL_WIDTH = 225; static final int PANEL_WIDTH = 225;
static final int SCROLLBAR_WIDTH = 17;
private static final int OFFSET = 6; private static final int OFFSET = 6;
private static final EmptyBorder BORDER_PADDING = new EmptyBorder(OFFSET, OFFSET, OFFSET, OFFSET); private static final EmptyBorder BORDER_PADDING = new EmptyBorder(OFFSET, OFFSET, OFFSET, OFFSET);
@Getter(AccessLevel.PROTECTED)
private final JScrollPane scrollPane;
@Getter(AccessLevel.PACKAGE)
private final JPanel wrappedPanel;
public PluginPanel() public PluginPanel()
{ {
super(); super();
setBorder(BORDER_PADDING); setBorder(BORDER_PADDING);
setLayout(new GridLayout(0, 1, 0, 3)); setLayout(new GridLayout(0, 1, 0, 3));
final JPanel northPanel = new JPanel();
northPanel.setLayout(new BorderLayout());
northPanel.add(this, BorderLayout.NORTH);
scrollPane = new JScrollPane(northPanel);
scrollPane.getVerticalScrollBar().setUnitIncrement(16); //Otherwise scrollspeed is really slow
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
wrappedPanel = new JPanel();
// Adjust the preferred size to expand to width of scrollbar to
// to preven scrollbar overlapping over contents
wrappedPanel.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH + SCROLLBAR_WIDTH, 0));
wrappedPanel.setLayout(new BorderLayout());
wrappedPanel.add(scrollPane, BorderLayout.CENTER);
} }
@Override @Override
@@ -47,4 +75,12 @@ public abstract class PluginPanel extends JPanel
{ {
return new Dimension(PANEL_WIDTH, super.getPreferredSize().height); return new Dimension(PANEL_WIDTH, super.getPreferredSize().height);
} }
public void onActivate()
{
}
public void onDeactivate()
{
}
} }

View File

@@ -88,12 +88,11 @@ public class PluginToolbar extends JToolBar
} }
else else
{ {
current = button;
current.setSelected(true);
PluginPanel pluginPanel = panelSupplier.get(); PluginPanel pluginPanel = panelSupplier.get();
ui.expand(pluginPanel); ui.expand(pluginPanel);
current = button;
current.setSelected(true);
} }
} }
} }