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.ImageUtil;
import net.runelite.client.util.QuantityFormatter;
import net.runelite.client.util.SwingUtil;
import net.runelite.http.api.loottracker.LootTrackerClient;
class LootTrackerPanel extends PluginPanel
@@ -488,7 +489,7 @@ class LootTrackerPanel extends PluginPanel
*/
private void rebuild()
{
logsContainer.removeAll();
SwingUtil.fastRemoveAll(logsContainer);
boxes.clear();
if (groupLoot)

View File

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

View File

@@ -26,11 +26,16 @@ package net.runelite.client.util;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Image;
import java.awt.Insets;
import java.awt.SecondaryLoop;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
@@ -50,6 +55,7 @@ import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
@@ -293,4 +299,54 @@ public class SwingUtil
{
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();
}
}
}