loottracker: Optimize panel rebuild

Removing components in swing can be incredibly slow under serveral
circumstances. This adds a special removeAll that can take less
time in some cases
This commit is contained in:
Max Weber
2020-01-13 17:59:43 -07:00
parent 38c6478c40
commit a5f5fe6940
3 changed files with 59 additions and 2 deletions

View File

@@ -54,6 +54,7 @@ import net.runelite.client.ui.components.PluginErrorPanel;
import net.runelite.client.util.ColorUtil; import net.runelite.client.util.ColorUtil;
import net.runelite.client.util.ImageUtil; import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.QuantityFormatter; import net.runelite.client.util.QuantityFormatter;
import net.runelite.client.util.SwingUtil;
import net.runelite.http.api.loottracker.LootTrackerClient; import net.runelite.http.api.loottracker.LootTrackerClient;
class LootTrackerPanel extends PluginPanel class LootTrackerPanel extends PluginPanel
@@ -488,7 +489,7 @@ class LootTrackerPanel extends PluginPanel
*/ */
private void rebuild() private void rebuild()
{ {
logsContainer.removeAll(); SwingUtil.fastRemoveAll(logsContainer);
boxes.clear(); boxes.clear();
if (groupLoot) if (groupLoot)

View File

@@ -612,7 +612,7 @@ public class LootTrackerPlugin extends Plugin
} }
config.setIgnoredItems(Text.toCSV(ignoredItemSet)); config.setIgnoredItems(Text.toCSV(ignoredItemSet));
panel.updateIgnoredRecords(); // the config changed will update the panel
} }
boolean isIgnored(String name) boolean isIgnored(String name)

View File

@@ -26,11 +26,16 @@ package net.runelite.client.util;
import java.awt.AWTException; import java.awt.AWTException;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Font; import java.awt.Font;
import java.awt.Frame; import java.awt.Frame;
import java.awt.Image; import java.awt.Image;
import java.awt.Insets; import java.awt.Insets;
import java.awt.SecondaryLoop;
import java.awt.SystemTray; import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon; import java.awt.TrayIcon;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
@@ -50,6 +55,7 @@ import javax.swing.JMenuItem;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager; import javax.swing.ToolTipManager;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException; import javax.swing.UnsupportedLookAndFeelException;
@@ -293,4 +299,54 @@ public class SwingUtil
{ {
button.addItemListener(l -> button.setToolTipText(button.isSelected() ? on : off)); button.addItemListener(l -> button.setToolTipText(button.isSelected() ? on : off));
} }
/**
* Removes all of a component's children faster than calling removeAll() on it in many cases
*/
public static void fastRemoveAll(Container c)
{
// If we are not on the EDT this will deadlock, in addition to being totally unsafe
assert SwingUtilities.isEventDispatchThread();
// when a component is removed it has to be resized for some reason, but only if it's valid
// so we make sure to invalidate everything before removing it
c.invalidate();
for (int i = 0; i < c.getComponentCount(); i++)
{
Component ic = c.getComponent(i);
// removeAll and removeNotify are both recursive, so we have to recurse before them
if (ic instanceof Container)
{
fastRemoveAll((Container) ic);
}
// each removeNotify needs to remove anything from the event queue that is for that widget
// this however requires taking a lock, and is moderately slow, so we just execute all of
// those events with a secondary event loop
pumpPendingEvents();
// call removeNotify early; this is most of the work in removeAll, and generates events that
// the next secondaryLoop will pickup
ic.removeNotify();
}
// Actually remove anything
c.removeAll();
}
/**
* Run any events currently in the event queue
*/
public static void pumpPendingEvents()
{
EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
if (eq.peekEvent() != null)
{
SecondaryLoop l = eq.createSecondaryLoop();
SwingUtilities.invokeLater(l::exit);
l.enter();
}
}
} }