Rework the Search popup and highlight what matched
- The search field is a single-line field, so Enter no longer adds a
line break that made every search come up empty.
- The field, button and list use real font sizes from the theme instead
of being drawn at half size and scaled up, so the text is sharp, and
everything fits inside the panel.
- Results have alternating row backgrounds like the other lists, a count
("3 of 1045 soundtracks") and a message when nothing matches.
- The letters that matched are highlighted.
- Searches shorter than Settings.LETTERS_FOR_LOOSE_SEARCH (3) only find
names that contain the text, instead of names with the letters far
apart, which matched most of the library. The Statistics tab uses the
same search.
This commit is contained in:
1 parent
13fdb3f68c
commit
845d08c260
12 files changed
+2063
-808
No files matched your search
@@ -1,7 +1,8 @@
|
||||
class_name SoundtrackSearch
|
||||
extends RefCounted
|
||||
## Finds soundtrack names matching a search text. Matches ignore case, spaces and
|
||||
## special characters, and also find the letters in order with others in between.
|
||||
## special characters. Searches of at least [code]Settings.LETTERS_FOR_LOOSE_SEARCH[/code]
|
||||
## letters also find the letters in order with others in between.
|
||||
##
|
||||
## Used by the Search popup and the statistics view, so both find the same soundtracks.
|
||||
|
||||
@@ -30,12 +31,14 @@ func find_matches(search_text: String) -> Array[String]:
|
||||
var seen: Dictionary = {}
|
||||
var search_text_lower: String = search_text.to_lower()
|
||||
var clean_search_text: String = clean_term(search_text)
|
||||
var regex: RegEx = _compile_regex_cached(clean_search_text)
|
||||
var passes: Array[Callable] = [
|
||||
func(entry: Dictionary) -> bool: return entry.lowercase.contains(search_text_lower),
|
||||
func(entry: Dictionary) -> bool: return entry.cleaned.contains(clean_search_text),
|
||||
func(entry: Dictionary) -> bool: return regex.search(entry.cleaned) != null,
|
||||
]
|
||||
if clean_search_text.length() >= Settings.LETTERS_FOR_LOOSE_SEARCH:
|
||||
var regex: RegEx = _compile_regex_cached(clean_search_text)
|
||||
passes.append(
|
||||
func(entry: Dictionary) -> bool: return regex.search(entry.cleaned) != null)
|
||||
|
||||
var matches: Array[String] = []
|
||||
for matches_pass: Callable in passes:
|
||||
@@ -46,6 +49,57 @@ func find_matches(search_text: String) -> Array[String]:
|
||||
return matches
|
||||
|
||||
|
||||
## The positions of the letters in [param soundtrack_name] that [param search_text]
|
||||
## matched, for highlighting them: the text itself if the name contains it, otherwise the
|
||||
## letters found in order the way [method find_matches] finds them. Empty if it does not
|
||||
## match.
|
||||
static func matched_letters(soundtrack_name: String, search_text: String) -> Array[int]:
|
||||
var positions: Array[int] = []
|
||||
if search_text.is_empty():
|
||||
return positions
|
||||
var start: int = soundtrack_name.to_lower().find(search_text.to_lower())
|
||||
if start >= 0:
|
||||
for i: int in search_text.length():
|
||||
positions.append(start + i)
|
||||
return positions
|
||||
|
||||
# Clean the name piece by piece, remembering which letter each cleaned letter came from
|
||||
var cleaned: String = ""
|
||||
var source: Array[int] = []
|
||||
var i: int = 0
|
||||
while i < soundtrack_name.length():
|
||||
var piece: String
|
||||
var length: int = 1
|
||||
if soundtrack_name.substr(i, 2).to_lower() == "'n":
|
||||
piece = "and"
|
||||
length = 2
|
||||
else:
|
||||
piece = clean_term(soundtrack_name[i])
|
||||
for letter: String in piece:
|
||||
cleaned += letter
|
||||
source.append(i)
|
||||
i += length
|
||||
|
||||
var clean_search_text: String = clean_term(search_text)
|
||||
var found: int = cleaned.find(clean_search_text)
|
||||
var cleaned_positions: Array[int] = []
|
||||
if found >= 0:
|
||||
for j: int in clean_search_text.length():
|
||||
cleaned_positions.append(found + j)
|
||||
elif clean_search_text.length() >= Settings.LETTERS_FOR_LOOSE_SEARCH:
|
||||
var at: int = 0
|
||||
for letter: String in clean_search_text:
|
||||
at = cleaned.find(letter, at)
|
||||
if at < 0:
|
||||
return positions
|
||||
cleaned_positions.append(at)
|
||||
at += 1
|
||||
for cleaned_position: int in cleaned_positions:
|
||||
if !positions.has(source[cleaned_position]):
|
||||
positions.append(source[cleaned_position])
|
||||
return positions
|
||||
|
||||
|
||||
## Cleans a term by removing spaces and replacing special characters.
|
||||
## Matches MusicServer's clean_term() function.
|
||||
static func clean_term(term: String) -> String:
|
||||
|
||||
Reference in new issue
Block a user