mirror of
https://github.com/GTNewHorizons/BetterP2P.git
synced 2026-10-26 14:24:05 +00:00
Merge pull request #20 from firenoo/dev/firenoo/filter
New Filtering System for Text Search
This commit is contained in:
4 files changed
+131
-7
No files matched your search
@@ -49,6 +49,16 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
|
||||
private var mode = msg.memoryInfo.mode
|
||||
private var modeString = getModeString()
|
||||
private val modeButton by lazy { GuiButton(0, guiLeft + 8, guiTop + 190, 256, 20, modeString) }
|
||||
private val sortRules: List<String> by lazy {
|
||||
listOf(
|
||||
"§b§n" + I18n.format("gui.advanced_memory_card.sortinfo1"),
|
||||
"§9@in§7 - " + I18n.format("gui.advanced_memory_card.sortinfo2"),
|
||||
"§6@out§7 - " + I18n.format("gui.advanced_memory_card.sortinfo3"),
|
||||
"§a@b§7 - " + I18n.format("gui.advanced_memory_card.sortinfo4"),
|
||||
"§c@u§7 - " + I18n.format("gui.advanced_memory_card.sortinfo5"),
|
||||
"§7" + I18n.format("gui.advanced_memory_card.sortinfo6")
|
||||
)
|
||||
}
|
||||
|
||||
private val selectedInfo: InfoWrapper?
|
||||
get() = infos.selectedInfo
|
||||
@@ -141,6 +151,9 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
|
||||
drawInformation()
|
||||
if(renameBar.isVisible && !renameBar.isFocused) renameBar.setFocus(true)
|
||||
renameBar.drawTextBox()
|
||||
if (searchBar.isMouseIn(mouseX, mouseY)) {
|
||||
drawHoveringText(sortRules, guiLeft, guiTop + ySize - 40, fontRendererObj)
|
||||
}
|
||||
super.drawScreen(mouseX, mouseY, partialTicks)
|
||||
}
|
||||
|
||||
@@ -321,8 +334,7 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
|
||||
}else{
|
||||
renameBar.textboxKeyTyped(char, key)
|
||||
}
|
||||
}else if (searchBar.isFocused && !(char == ' ' && searchBar.text.isEmpty())){
|
||||
searchBar.textboxKeyTyped(char, key)
|
||||
}else if (!(char.isWhitespace() && searchBar.text.isEmpty()) && searchBar.textboxKeyTyped(char, key)){
|
||||
infos.refilter()
|
||||
}
|
||||
return super.keyTyped(char, key)
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.projecturanus.betterp2p.client.gui
|
||||
|
||||
/**
|
||||
* Extend the default filtering. Holds information about the search filter.
|
||||
*
|
||||
* There are 3 modes of filtering:
|
||||
* - By input/output: Using `@in/@out` filters by input/output respectively.
|
||||
* - By bound/unbound: Use `@b` or `@u` filters by input/output respectively.
|
||||
* - By name: Use the name
|
||||
* If someone comes and says "sort by freq pls" then we can add it at that time
|
||||
*/
|
||||
class InfoFilter {
|
||||
|
||||
/**
|
||||
* Active filters to use when filtering entries.
|
||||
*/
|
||||
val activeFilters: MutableMap<Filter, MutableList<String>?> = mutableMapOf()
|
||||
|
||||
/**
|
||||
* Parse the query string for filters and update the active
|
||||
* filter list.
|
||||
*/
|
||||
fun updateFilter(query: String) {
|
||||
val tokens = SEARCH_REGEX.findAll(query)
|
||||
activeFilters.clear()
|
||||
tokens.forEach {
|
||||
when {
|
||||
it.value.matches(Filter.INPUT.pattern) -> {
|
||||
activeFilters.putIfAbsent(Filter.INPUT, null)
|
||||
}
|
||||
it.value.matches(Filter.OUTPUT.pattern) -> {
|
||||
activeFilters.putIfAbsent(Filter.OUTPUT, null)
|
||||
}
|
||||
it.value.matches(Filter.BOUND.pattern) -> {
|
||||
activeFilters.putIfAbsent(Filter.BOUND, null)
|
||||
}
|
||||
it.value.matches(Filter.UNBOUND.pattern) -> {
|
||||
activeFilters.putIfAbsent(Filter.UNBOUND, null)
|
||||
}
|
||||
it.value.isBlank() -> {}
|
||||
else -> {
|
||||
activeFilters.putIfAbsent(Filter.NAME, mutableListOf())
|
||||
when {
|
||||
it.groups[1] != null -> activeFilters[Filter.NAME]!!.add(it.groups[1]!!.value)
|
||||
it.groups[2] != null -> activeFilters[Filter.NAME]!!.add(it.groups[2]!!.value)
|
||||
else -> activeFilters[Filter.NAME]!!.add(it.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The different filter types. Probably will let these be adjustable in the
|
||||
* config.
|
||||
*/
|
||||
enum class Filter(val pattern: Regex, val filter: (InfoWrapper, List<String>?) -> Boolean) {
|
||||
INPUT("\\A@in\\z".toRegex(), { it, _ -> !it.output }),
|
||||
OUTPUT("\\A@out\\z".toRegex(), { it, _ -> it.output }),
|
||||
BOUND("\\A@b\\z".toRegex(), { it, _ -> it.frequency != 0L }),
|
||||
UNBOUND("\\A@u\\z".toRegex(), { it, _ -> it.frequency == 0L || it.error }),
|
||||
NAME("\"?.+\"?".toRegex(), filter@{ it, strs ->
|
||||
val name = it.name.lowercase()
|
||||
for (f in strs!!) {
|
||||
// Ppl better not troll and use double quotes in their P2P tunnel names
|
||||
val query = f.removeSurrounding("\"")
|
||||
if (name.contains(query)) {
|
||||
return@filter true
|
||||
}
|
||||
}
|
||||
false
|
||||
});
|
||||
}
|
||||
|
||||
// I spent 10 minutes on this until I gave up... regex wtf
|
||||
// https://stackoverflow.com/questions/366202/regex-for-splitting-a-string-using-space-when-not-surrounded-by-single-or-double
|
||||
val SEARCH_REGEX = "[^\\s\"']+|\"([^\"]*)\"|'([^']*)'".toRegex()
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.projecturanus.betterp2p.client.gui
|
||||
|
||||
import com.projecturanus.betterp2p.network.NONE
|
||||
import com.projecturanus.betterp2p.network.hashP2P
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.reflect.KProperty0
|
||||
|
||||
/**
|
||||
@@ -31,6 +32,8 @@ class InfoList (initList: Collection<InfoWrapper>,
|
||||
*/
|
||||
var filtered: List<InfoWrapper> = listOf()
|
||||
|
||||
private val filter: InfoFilter = InfoFilter()
|
||||
|
||||
/**
|
||||
* Binding to the search string in the text box
|
||||
*/
|
||||
@@ -59,7 +62,7 @@ class InfoList (initList: Collection<InfoWrapper>,
|
||||
} else if (it.frequency != 0.toLong() && it.frequency == selectedInfo?.frequency) {
|
||||
-1 // Put same frequency in the front
|
||||
} else {
|
||||
// Frequencies from highest to lowest
|
||||
// Frequencies from lowest to highest
|
||||
it.frequency + Short.MAX_VALUE
|
||||
}
|
||||
}
|
||||
@@ -69,11 +72,35 @@ class InfoList (initList: Collection<InfoWrapper>,
|
||||
* Updates the filtered list.
|
||||
*/
|
||||
fun refilter() {
|
||||
filter.updateFilter(searchStr.lowercase())
|
||||
filtered = sorted.filter {
|
||||
it.frequency.toHexString().contains(searchStr.uppercase()) ||
|
||||
it.frequency.toHexString().format4().contains(searchStr.uppercase()) ||
|
||||
it.name.lowercase().contains(searchStr.lowercase()) ||
|
||||
it.code == selectedEntry
|
||||
if (it.code == selectedEntry) {
|
||||
return@filter true
|
||||
}
|
||||
for ((f, strs) in filter.activeFilters) {
|
||||
if(!f.filter(it, strs?.toList())) {
|
||||
return@filter false
|
||||
}
|
||||
}
|
||||
true
|
||||
}.sortedBy {
|
||||
when {
|
||||
it.code == selectedEntry -> Long.MIN_VALUE + 1
|
||||
it.frequency != 0L && it.frequency == selectedInfo?.frequency && !it.output -> Long.MIN_VALUE
|
||||
it.frequency != 0.toLong() && it.frequency == selectedInfo?.frequency -> Long.MIN_VALUE + 2 // Put same frequency in the front
|
||||
filter.activeFilters.containsKey(Filter.NAME) -> {
|
||||
var hits = 0L
|
||||
var name = it.name
|
||||
for (f in filter.activeFilters[Filter.NAME]!!) {
|
||||
if (name.contains(f, true)) {
|
||||
hits += 1
|
||||
name = name.replaceFirst(f, "", true)
|
||||
}
|
||||
}
|
||||
-(hits * hits) + name.length
|
||||
}
|
||||
else -> it.frequency + Short.MAX_VALUE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,12 @@ gui.advanced_memory_card.select=Select
|
||||
gui.advanced_memory_card.bind=Bind
|
||||
gui.advanced_memory_card.pos=Pos: %d, %d, %d Side: %s
|
||||
gui.advanced_memory_card.name=Name: %s
|
||||
gui.advanced_memory_card.sortinfo1=Sorting tags
|
||||
gui.advanced_memory_card.sortinfo2=Show Input P2Ps only
|
||||
gui.advanced_memory_card.sortinfo3=Show Output P2Ps only
|
||||
gui.advanced_memory_card.sortinfo4=Show Bound P2Ps only
|
||||
gui.advanced_memory_card.sortinfo5=Show Unbound P2Ps only
|
||||
gui.advanced_memory_card.sortinfo6=Tags are space separated and are combined w/ the AND operator.
|
||||
gui.advanced_memory_card.extra.channel=Channels used: %s
|
||||
gui.advanced_memory_card.extra.none=No Extra Info
|
||||
gui.advanced_memory_card.mode.input=Mode: Bind Input
|
||||
|
||||
Reference in new issue
Block a user