mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 03:30:13 +01:00
add search
This commit is contained in:
4
Songs/18 Search/box.def
Normal file
4
Songs/18 Search/box.def
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#TITLE:Search Song
|
||||||
|
#TITLEJA:曲検索
|
||||||
|
#COLLECTION:SEARCH
|
||||||
|
#BACKCOLOR:#800000
|
||||||
@@ -1038,7 +1038,8 @@ class Directory(FileSystemItem):
|
|||||||
'RECENT',
|
'RECENT',
|
||||||
'FAVORITE',
|
'FAVORITE',
|
||||||
'DIFFICULTY',
|
'DIFFICULTY',
|
||||||
'RECOMMENDED'
|
'RECOMMENDED',
|
||||||
|
'SEARCH'
|
||||||
]
|
]
|
||||||
def __init__(self, path: Path, name: str, back_color: Optional[tuple[int, int, int]], fore_color: Optional[tuple[int, int, int]], texture_index: TextureIndex, genre_index: GenreIndex, has_box_def=False, to_root=False, back=False, tja_count=0, box_texture=None, collection=None):
|
def __init__(self, path: Path, name: str, back_color: Optional[tuple[int, int, int]], fore_color: Optional[tuple[int, int, int]], texture_index: TextureIndex, genre_index: GenreIndex, has_box_def=False, to_root=False, back=False, tja_count=0, box_texture=None, collection=None):
|
||||||
super().__init__(path, name)
|
super().__init__(path, name)
|
||||||
@@ -1143,6 +1144,7 @@ class FileNavigator:
|
|||||||
self.genre_bg = None
|
self.genre_bg = None
|
||||||
self.song_count = 0
|
self.song_count = 0
|
||||||
self.in_dan_select = False
|
self.in_dan_select = False
|
||||||
|
self.current_search = ''
|
||||||
logger.info("FileNavigator initialized")
|
logger.info("FileNavigator initialized")
|
||||||
|
|
||||||
def initialize(self, root_dirs: list[Path]):
|
def initialize(self, root_dirs: list[Path]):
|
||||||
@@ -1392,6 +1394,43 @@ class FileNavigator:
|
|||||||
temp_items.append(item)
|
temp_items.append(item)
|
||||||
return random.sample(temp_items, min(10, len(temp_items)))
|
return random.sample(temp_items, min(10, len(temp_items)))
|
||||||
|
|
||||||
|
def _levenshtein_distance(self, s1: str, s2: str):
|
||||||
|
# Create a matrix to store distances
|
||||||
|
m, n = len(s1), len(s2)
|
||||||
|
dp = [[0] * (n + 1) for _ in range(m + 1)]
|
||||||
|
|
||||||
|
# Initialize base cases
|
||||||
|
for i in range(m + 1):
|
||||||
|
dp[i][0] = i
|
||||||
|
for j in range(n + 1):
|
||||||
|
dp[0][j] = j
|
||||||
|
|
||||||
|
# Fill the matrix
|
||||||
|
for i in range(1, m + 1):
|
||||||
|
for j in range(1, n + 1):
|
||||||
|
if s1[i-1] == s2[j-1]:
|
||||||
|
dp[i][j] = dp[i-1][j-1] # No operation needed
|
||||||
|
else:
|
||||||
|
dp[i][j] = 1 + min(
|
||||||
|
dp[i-1][j], # Deletion
|
||||||
|
dp[i][j-1], # Insertion
|
||||||
|
dp[i-1][j-1] # Substitution
|
||||||
|
)
|
||||||
|
|
||||||
|
return dp[m][n]
|
||||||
|
|
||||||
|
def search_song(self, search_name: str):
|
||||||
|
items = []
|
||||||
|
for path, song in self.all_song_files.items():
|
||||||
|
logger.info(f"{song.name[:-4].lower()}, {search_name.lower()}, {self._levenshtein_distance(song.name[:-4].lower(), search_name.lower())}")
|
||||||
|
if self._levenshtein_distance(song.name[:-4].lower(), search_name.lower()) < 2:
|
||||||
|
items.append(song)
|
||||||
|
if isinstance(song, SongFile):
|
||||||
|
if self._levenshtein_distance(song.tja.metadata.subtitle["en"].lower(), search_name.lower()) < 2:
|
||||||
|
items.append(song)
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
def load_current_directory(self, selected_item: Optional[Directory] = None):
|
def load_current_directory(self, selected_item: Optional[Directory] = None):
|
||||||
"""Load pre-generated items for the current directory (unified for root and subdirs)"""
|
"""Load pre-generated items for the current directory (unified for root and subdirs)"""
|
||||||
dir_key = str(self.current_dir)
|
dir_key = str(self.current_dir)
|
||||||
@@ -1438,6 +1477,8 @@ class FileNavigator:
|
|||||||
content_items = self.load_diff_sort_items(selected_item, dir_key)
|
content_items = self.load_diff_sort_items(selected_item, dir_key)
|
||||||
elif selected_item.collection == Directory.COLLECTIONS[4]:
|
elif selected_item.collection == Directory.COLLECTIONS[4]:
|
||||||
content_items = self.load_recommended_items(selected_item, dir_key)
|
content_items = self.load_recommended_items(selected_item, dir_key)
|
||||||
|
elif selected_item.collection == Directory.COLLECTIONS[5]:
|
||||||
|
content_items = self.search_song(self.current_search)
|
||||||
|
|
||||||
if content_items == []:
|
if content_items == []:
|
||||||
self.go_back()
|
self.go_back()
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class State:
|
|||||||
BROWSING = 0
|
BROWSING = 0
|
||||||
SONG_SELECTED = 1
|
SONG_SELECTED = 1
|
||||||
DIFF_SORTING = 2
|
DIFF_SORTING = 2
|
||||||
|
SEARCHING = 3
|
||||||
|
|
||||||
class SongSelectScreen(Screen):
|
class SongSelectScreen(Screen):
|
||||||
BOX_CENTER = 444
|
BOX_CENTER = 444
|
||||||
@@ -69,6 +70,7 @@ class SongSelectScreen(Screen):
|
|||||||
self.game_transition = None
|
self.game_transition = None
|
||||||
self.demo_song = None
|
self.demo_song = None
|
||||||
self.diff_sort_selector = None
|
self.diff_sort_selector = None
|
||||||
|
self.search_box = None
|
||||||
self.coin_overlay = CoinOverlay()
|
self.coin_overlay = CoinOverlay()
|
||||||
self.allnet_indicator = AllNetIcon()
|
self.allnet_indicator = AllNetIcon()
|
||||||
self.indicator = Indicator(Indicator.State.SELECT)
|
self.indicator = Indicator(Indicator.State.SELECT)
|
||||||
@@ -167,6 +169,11 @@ class SongSelectScreen(Screen):
|
|||||||
self.diff_sort_selector = DiffSortSelect(self.navigator.diff_sort_statistics, self.navigator.diff_sort_diff, self.navigator.diff_sort_level)
|
self.diff_sort_selector = DiffSortSelect(self.navigator.diff_sort_statistics, self.navigator.diff_sort_diff, self.navigator.diff_sort_level)
|
||||||
self.text_fade_in.start()
|
self.text_fade_in.start()
|
||||||
self.text_fade_out.start()
|
self.text_fade_out.start()
|
||||||
|
elif action == "search":
|
||||||
|
self.state = State.SEARCHING
|
||||||
|
self.search_box = SearchBox()
|
||||||
|
self.text_fade_in.start()
|
||||||
|
self.text_fade_out.start()
|
||||||
elif action == "select_song":
|
elif action == "select_song":
|
||||||
current_song = self.navigator.get_current_item()
|
current_song = self.navigator.get_current_item()
|
||||||
if isinstance(current_song, Directory) and current_song.box.genre_index == GenreIndex.DAN:
|
if isinstance(current_song, Directory) and current_song.box.genre_index == GenreIndex.DAN:
|
||||||
@@ -225,6 +232,21 @@ class SongSelectScreen(Screen):
|
|||||||
self.navigator.diff_sort_level = level
|
self.navigator.diff_sort_level = level
|
||||||
self.navigator.select_current_item()
|
self.navigator.select_current_item()
|
||||||
|
|
||||||
|
def handle_input_search(self):
|
||||||
|
if self.search_box is None:
|
||||||
|
raise Exception("search box was not able to be created")
|
||||||
|
|
||||||
|
result = self.player_1.handle_input_search()
|
||||||
|
self.search_box.current_search = self.player_1.search_string
|
||||||
|
|
||||||
|
if result is not None:
|
||||||
|
self.state = State.BROWSING
|
||||||
|
self.search_box = None
|
||||||
|
self.text_fade_out.reset()
|
||||||
|
self.text_fade_in.reset()
|
||||||
|
self.navigator.current_search = result
|
||||||
|
self.navigator.select_current_item()
|
||||||
|
|
||||||
def _cancel_selection(self):
|
def _cancel_selection(self):
|
||||||
"""Reset to browsing state"""
|
"""Reset to browsing state"""
|
||||||
self.player_1.selected_song = False
|
self.player_1.selected_song = False
|
||||||
@@ -324,6 +346,9 @@ class SongSelectScreen(Screen):
|
|||||||
if self.diff_sort_selector is not None:
|
if self.diff_sort_selector is not None:
|
||||||
self.diff_sort_selector.update(current_time)
|
self.diff_sort_selector.update(current_time)
|
||||||
|
|
||||||
|
if self.search_box is not None:
|
||||||
|
self.search_box.update(current_time)
|
||||||
|
|
||||||
self.check_for_selection()
|
self.check_for_selection()
|
||||||
|
|
||||||
for song in self.navigator.items:
|
for song in self.navigator.items:
|
||||||
@@ -386,6 +411,9 @@ class SongSelectScreen(Screen):
|
|||||||
if self.diff_sort_selector is not None:
|
if self.diff_sort_selector is not None:
|
||||||
self.diff_sort_selector.draw()
|
self.diff_sort_selector.draw()
|
||||||
|
|
||||||
|
if self.search_box is not None:
|
||||||
|
self.search_box.draw()
|
||||||
|
|
||||||
if (self.player_1.selected_song and self.state == State.SONG_SELECTED):
|
if (self.player_1.selected_song and self.state == State.SONG_SELECTED):
|
||||||
tex.draw_texture('global', 'difficulty_select', fade=self.text_fade_in.attribute)
|
tex.draw_texture('global', 'difficulty_select', fade=self.text_fade_in.attribute)
|
||||||
elif self.state == State.DIFF_SORTING:
|
elif self.state == State.DIFF_SORTING:
|
||||||
@@ -428,6 +456,7 @@ class SongSelectPlayer:
|
|||||||
self.diff_select_move_right = False
|
self.diff_select_move_right = False
|
||||||
self.neiro_selector = None
|
self.neiro_selector = None
|
||||||
self.modifier_selector = None
|
self.modifier_selector = None
|
||||||
|
self.search_string = ''
|
||||||
|
|
||||||
# References to shared animations
|
# References to shared animations
|
||||||
self.diff_selector_move_1 = tex.get_animation(26, is_copy=True)
|
self.diff_selector_move_1 = tex.get_animation(26, is_copy=True)
|
||||||
@@ -511,6 +540,8 @@ class SongSelectPlayer:
|
|||||||
return "go_back"
|
return "go_back"
|
||||||
elif isinstance(selected_item, Directory) and selected_item.collection == Directory.COLLECTIONS[3]:
|
elif isinstance(selected_item, Directory) and selected_item.collection == Directory.COLLECTIONS[3]:
|
||||||
return "diff_sort"
|
return "diff_sort"
|
||||||
|
elif isinstance(selected_item, Directory) and selected_item.collection == Directory.COLLECTIONS[5]:
|
||||||
|
return "search"
|
||||||
else:
|
else:
|
||||||
return "select_song"
|
return "select_song"
|
||||||
|
|
||||||
@@ -538,6 +569,20 @@ class SongSelectPlayer:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def handle_input_search(self):
|
||||||
|
if ray.is_key_pressed(ray.KeyboardKey.KEY_BACKSPACE):
|
||||||
|
self.search_string = self.search_string[:-1]
|
||||||
|
elif ray.is_key_pressed(ray.KeyboardKey.KEY_ENTER):
|
||||||
|
result = self.search_string
|
||||||
|
self.search_string = ''
|
||||||
|
return result
|
||||||
|
key = ray.get_char_pressed()
|
||||||
|
|
||||||
|
while key > 0:
|
||||||
|
self.search_string += chr(key)
|
||||||
|
key = ray.get_char_pressed()
|
||||||
|
return None
|
||||||
|
|
||||||
def handle_input(self, state, screen):
|
def handle_input(self, state, screen):
|
||||||
"""Main input dispatcher. Delegates to state-specific handlers."""
|
"""Main input dispatcher. Delegates to state-specific handlers."""
|
||||||
if self.is_voice_playing() or self.is_ready:
|
if self.is_voice_playing() or self.is_ready:
|
||||||
@@ -549,6 +594,8 @@ class SongSelectPlayer:
|
|||||||
screen.handle_input_selected()
|
screen.handle_input_selected()
|
||||||
elif state == State.DIFF_SORTING:
|
elif state == State.DIFF_SORTING:
|
||||||
screen.handle_input_diff_sort()
|
screen.handle_input_diff_sort()
|
||||||
|
elif state == State.SEARCHING:
|
||||||
|
screen.handle_input_search()
|
||||||
|
|
||||||
def handle_input_selected(self, current_item):
|
def handle_input_selected(self, current_item):
|
||||||
"""Handle input for selecting difficulty. Returns 'cancel', 'confirm', or None"""
|
"""Handle input for selecting difficulty. Returns 'cancel', 'confirm', or None"""
|
||||||
@@ -1027,6 +1074,31 @@ class DiffSortSelect:
|
|||||||
else:
|
else:
|
||||||
self.draw_diff_select()
|
self.draw_diff_select()
|
||||||
|
|
||||||
|
class SearchBox:
|
||||||
|
def __init__(self):
|
||||||
|
self.bg_resize = tex.get_animation(19)
|
||||||
|
self.diff_fade_in = tex.get_animation(20)
|
||||||
|
self.bg_resize.start()
|
||||||
|
self.diff_fade_in.start()
|
||||||
|
self.current_search = ''
|
||||||
|
|
||||||
|
def update(self, current_ms):
|
||||||
|
self.bg_resize.update(current_ms)
|
||||||
|
self.diff_fade_in.update(current_ms)
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
ray.draw_rectangle(0, 0, tex.screen_width, tex.screen_height, ray.fade(ray.BLACK, 0.6))
|
||||||
|
tex.draw_texture('diff_sort', 'background', scale=self.bg_resize.attribute, center=True)
|
||||||
|
background = tex.textures['diff_sort']['background']
|
||||||
|
fade = self.diff_fade_in.attribute
|
||||||
|
text_box_width, text_box_height = 400 * tex.screen_scale, 60 * tex.screen_scale
|
||||||
|
x, y = background.width//2 + background.x[0] - text_box_width//2, background.height//2 + background.y[0] - text_box_height//2
|
||||||
|
text_box = ray.Rectangle(x, y, text_box_width, text_box_height)
|
||||||
|
ray.draw_rectangle_rec(text_box, ray.fade(ray.LIGHTGRAY, fade))
|
||||||
|
ray.draw_rectangle_lines(int(text_box.x), int(text_box.y), int(text_box.width), int(text_box.height), ray.fade(ray.DARKGRAY, fade))
|
||||||
|
text_size = ray.measure_text_ex(global_data.font, self.current_search, int(30 * tex.screen_scale), 1)
|
||||||
|
ray.draw_text_ex(global_data.font, self.current_search, ray.Vector2(x + text_box_width//2 - text_size.x//2, y + text_box_height//2 - text_size.y//2), int(30 * tex.screen_scale), 1, ray.BLACK)
|
||||||
|
|
||||||
class NeiroSelector:
|
class NeiroSelector:
|
||||||
"""The menu for selecting the game hitsounds."""
|
"""The menu for selecting the game hitsounds."""
|
||||||
def __init__(self, player_num: PlayerNum):
|
def __init__(self, player_num: PlayerNum):
|
||||||
|
|||||||
Reference in New Issue
Block a user