Merge pull request #10894 from loldudester/external-searching
Plugin Hub: Reorder plugin panel and rework the search function
This commit is contained in:
@@ -29,6 +29,7 @@ import com.google.common.io.Files;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import net.runelite.client.RuneLite;
|
import net.runelite.client.RuneLite;
|
||||||
@@ -45,7 +46,9 @@ public class ExternalPluginManifest
|
|||||||
private String displayName;
|
private String displayName;
|
||||||
private String version;
|
private String version;
|
||||||
private String author;
|
private String author;
|
||||||
|
@Nullable
|
||||||
private String description;
|
private String description;
|
||||||
|
@Nullable
|
||||||
private String[] tags;
|
private String[] tags;
|
||||||
@EqualsAndHashCode.Exclude
|
@EqualsAndHashCode.Exclude
|
||||||
private URL support;
|
private URL support;
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ import java.awt.event.ActionEvent;
|
|||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -45,7 +47,6 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.ToDoubleFunction;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@@ -85,8 +86,8 @@ import net.runelite.client.ui.components.IconTextField;
|
|||||||
import net.runelite.client.util.ImageUtil;
|
import net.runelite.client.util.ImageUtil;
|
||||||
import net.runelite.client.util.LinkBrowser;
|
import net.runelite.client.util.LinkBrowser;
|
||||||
import net.runelite.client.util.SwingUtil;
|
import net.runelite.client.util.SwingUtil;
|
||||||
|
import net.runelite.client.util.Text;
|
||||||
import net.runelite.client.util.VerificationException;
|
import net.runelite.client.util.VerificationException;
|
||||||
import org.apache.commons.text.similarity.JaroWinklerDistance;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -98,7 +99,6 @@ class PluginHubPanel extends PluginPanel
|
|||||||
private static final ImageIcon CONFIGURE_ICON;
|
private static final ImageIcon CONFIGURE_ICON;
|
||||||
private static final ImageIcon CONFIGURE_ICON_HOVER;
|
private static final ImageIcon CONFIGURE_ICON_HOVER;
|
||||||
private static final Pattern SPACES = Pattern.compile(" +");
|
private static final Pattern SPACES = Pattern.compile(" +");
|
||||||
private static final JaroWinklerDistance DISTANCE = new JaroWinklerDistance();
|
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
@@ -119,16 +119,13 @@ class PluginHubPanel extends PluginPanel
|
|||||||
private static final int HEIGHT = 70;
|
private static final int HEIGHT = 70;
|
||||||
private static final int ICON_WIDTH = 48;
|
private static final int ICON_WIDTH = 48;
|
||||||
private static final int BOTTOM_LINE_HEIGHT = 16;
|
private static final int BOTTOM_LINE_HEIGHT = 16;
|
||||||
static final float MIN_FILTER_SCORE = .8f;
|
|
||||||
|
|
||||||
private final ExternalPluginManifest manifest;
|
private final ExternalPluginManifest manifest;
|
||||||
|
private final List<String> keywords = new ArrayList<>();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final boolean installed;
|
private final boolean installed;
|
||||||
|
|
||||||
@Getter
|
|
||||||
private float filter;
|
|
||||||
|
|
||||||
PluginItem(ExternalPluginManifest newManifest, Collection<Plugin> loadedPlugins, boolean installed)
|
PluginItem(ExternalPluginManifest newManifest, Collection<Plugin> loadedPlugins, boolean installed)
|
||||||
{
|
{
|
||||||
ExternalPluginManifest loaded = null;
|
ExternalPluginManifest loaded = null;
|
||||||
@@ -140,6 +137,23 @@ class PluginHubPanel extends PluginPanel
|
|||||||
manifest = newManifest == null ? loaded : newManifest;
|
manifest = newManifest == null ? loaded : newManifest;
|
||||||
this.installed = installed;
|
this.installed = installed;
|
||||||
|
|
||||||
|
if (manifest != null)
|
||||||
|
{
|
||||||
|
Collections.addAll(keywords, SPACES.split(manifest.getDisplayName().toLowerCase()));
|
||||||
|
|
||||||
|
if (manifest.getDescription() != null)
|
||||||
|
{
|
||||||
|
Collections.addAll(keywords, SPACES.split(manifest.getDescription().toLowerCase()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.addAll(keywords, manifest.getAuthor().toLowerCase());
|
||||||
|
|
||||||
|
if (manifest.getTags() != null)
|
||||||
|
{
|
||||||
|
Collections.addAll(keywords, manifest.getTags());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
setOpaque(true);
|
setOpaque(true);
|
||||||
|
|
||||||
@@ -302,23 +316,6 @@ class PluginHubPanel extends PluginPanel
|
|||||||
.addComponent(addrm, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT))
|
.addComponent(addrm, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT))
|
||||||
.addGap(5)));
|
.addGap(5)));
|
||||||
}
|
}
|
||||||
|
|
||||||
float setFilter(String[] filter)
|
|
||||||
{
|
|
||||||
ToDoubleFunction<String> match = r -> Stream.of(filter)
|
|
||||||
.mapToDouble(l -> Math.pow(DISTANCE.apply(l, r), 2))
|
|
||||||
.max()
|
|
||||||
.orElse(0.D);
|
|
||||||
|
|
||||||
double sim = SPACES.splitAsStream(manifest.getDisplayName()).collect(Collectors.averagingDouble(match)) * 2;
|
|
||||||
|
|
||||||
if (manifest.getTags() != null)
|
|
||||||
{
|
|
||||||
sim += Stream.of(manifest.getTags()).mapToDouble(match).sum();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.filter = (float) sim;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final PluginListPanel pluginListPanel;
|
private final PluginListPanel pluginListPanel;
|
||||||
@@ -537,13 +534,13 @@ class PluginHubPanel extends PluginPanel
|
|||||||
{
|
{
|
||||||
String[] searchArray = SPACES.split(search.toLowerCase());
|
String[] searchArray = SPACES.split(search.toLowerCase());
|
||||||
stream = stream
|
stream = stream
|
||||||
.filter(p -> p.setFilter(searchArray) > PluginItem.MIN_FILTER_SCORE)
|
.filter(p -> Text.matchesSearchTerms(searchArray, p.keywords))
|
||||||
.sorted(Comparator.comparing(PluginItem::getFilter));
|
.sorted(Comparator.comparing(p -> p.manifest.getDisplayName()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
stream = stream
|
stream = stream
|
||||||
.sorted(Comparator.comparing(PluginItem::isInstalled));
|
.sorted(Comparator.comparing(PluginItem::isInstalled).thenComparing(p -> p.manifest.getDisplayName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.forEach(mainPanel::add);
|
stream.forEach(mainPanel::add);
|
||||||
|
|||||||
@@ -52,12 +52,9 @@ import net.runelite.client.ui.ColorScheme;
|
|||||||
import net.runelite.client.ui.PluginPanel;
|
import net.runelite.client.ui.PluginPanel;
|
||||||
import net.runelite.client.util.ImageUtil;
|
import net.runelite.client.util.ImageUtil;
|
||||||
import net.runelite.client.util.SwingUtil;
|
import net.runelite.client.util.SwingUtil;
|
||||||
import org.apache.commons.text.similarity.JaroWinklerDistance;
|
|
||||||
|
|
||||||
class PluginListItem extends JPanel
|
class PluginListItem extends JPanel
|
||||||
{
|
{
|
||||||
private static final JaroWinklerDistance DISTANCE = new JaroWinklerDistance();
|
|
||||||
|
|
||||||
private static final ImageIcon CONFIG_ICON;
|
private static final ImageIcon CONFIG_ICON;
|
||||||
private static final ImageIcon CONFIG_ICON_HOVER;
|
private static final ImageIcon CONFIG_ICON_HOVER;
|
||||||
private static final ImageIcon ON_STAR;
|
private static final ImageIcon ON_STAR;
|
||||||
@@ -68,6 +65,7 @@ class PluginListItem extends JPanel
|
|||||||
@Getter
|
@Getter
|
||||||
private final PluginConfigurationDescriptor pluginConfig;
|
private final PluginConfigurationDescriptor pluginConfig;
|
||||||
|
|
||||||
|
@Getter
|
||||||
private final List<String> keywords = new ArrayList<>();
|
private final List<String> keywords = new ArrayList<>();
|
||||||
|
|
||||||
private final JToggleButton pinButton;
|
private final JToggleButton pinButton;
|
||||||
@@ -202,24 +200,6 @@ class PluginListItem extends JPanel
|
|||||||
onOffToggle.setSelected(enabled);
|
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)
|
|
||||||
{
|
|
||||||
for (String term : searchTerms)
|
|
||||||
{
|
|
||||||
if (keywords.stream().noneMatch((t) -> t.contains(term) ||
|
|
||||||
DISTANCE.apply(t, term) > 0.9))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openGroupConfigPanel()
|
private void openGroupConfigPanel()
|
||||||
{
|
{
|
||||||
pluginListPanel.openConfigurationPanel(pluginConfig);
|
pluginListPanel.openConfigurationPanel(pluginConfig);
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ class PluginListPanel extends PluginPanel
|
|||||||
final String[] searchTerms = text.toLowerCase().split(" ");
|
final String[] searchTerms = text.toLowerCase().split(" ");
|
||||||
pluginList.forEach(listItem ->
|
pluginList.forEach(listItem ->
|
||||||
{
|
{
|
||||||
if (pinned == listItem.isPinned() && listItem.matchesSearchTerms(searchTerms))
|
if (pinned == listItem.isPinned() && Text.matchesSearchTerms(searchTerms, listItem.getKeywords()))
|
||||||
{
|
{
|
||||||
mainPanel.add(listItem);
|
mainPanel.add(listItem);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,14 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import org.apache.commons.text.WordUtils;
|
import org.apache.commons.text.WordUtils;
|
||||||
|
import org.apache.commons.text.similarity.JaroWinklerDistance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of utilities to use when dealing with text.
|
* A set of utilities to use when dealing with text.
|
||||||
*/
|
*/
|
||||||
public class Text
|
public class Text
|
||||||
{
|
{
|
||||||
|
private static final JaroWinklerDistance DISTANCE = new JaroWinklerDistance();
|
||||||
private static final Pattern TAG_REGEXP = Pattern.compile("<[^>]*>");
|
private static final Pattern TAG_REGEXP = Pattern.compile("<[^>]*>");
|
||||||
private static final Splitter COMMA_SPLITTER = Splitter
|
private static final Splitter COMMA_SPLITTER = Splitter
|
||||||
.on(",")
|
.on(",")
|
||||||
@@ -186,4 +188,22 @@ public class Text
|
|||||||
|
|
||||||
return toString;
|
return toString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public static boolean matchesSearchTerms(String[] searchTerms, final Collection<String> keywords)
|
||||||
|
{
|
||||||
|
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