Replace game filter with more efficient version.

This will just leave the list alone if 'Installed games only' is off and the filter text is empty.

Also, it no longer strips the filter text over and over again, but just once at the start. It still has to strip each game name, but it's half the strippings now.

Also, fix some ruff issues.
This commit is contained in:
Daniel Johnson 2024-03-21 18:36:12 -04:00
parent 7689a74660
commit 44c2419ac1
3 changed files with 46 additions and 33 deletions

View file

@ -408,19 +408,30 @@ class LutrisWindow(Gtk.ApplicationWindow, DialogLaunchUIDelegate, DialogInstallU
def get_recent_games(self):
"""Return a list of currently running games"""
games = games_db.get_games(filters={"installed": "1"})
games = [game for game in games if self.game_matches(game)]
games = self.filter_games(games)
return sorted(games, key=lambda game: max(game["installed_at"] or 0, game["lastplayed"] or 0), reverse=True)
def game_matches(self, game):
if self.filters.get("installed"):
if "appid" in game and game["appid"] not in games_db.get_service_games(self.service.id):
return False
def filter_games(self, games):
"""Filters a list of games according to the 'installed' and 'text' filters, if those are
set. But if not, can just return games unchanged."""
installed = bool(self.filters.get("installed"))
text = self.filters.get("text")
if not text:
return True
text = strip_accents(text).casefold()
name = strip_accents(game["name"]).casefold()
return text in name
if text:
text = strip_accents(text).casefold()
def game_matches(game):
if installed:
if "appid" in game and game["appid"] not in games_db.get_service_games(self.service.id):
return False
if not text:
return True
name = strip_accents(game["name"]).casefold()
return text in name
if installed or text:
return [game for game in games if game_matches(game)]
return games
def set_service(self, service_name):
if self.service and self.service.id == service_name:
@ -451,11 +462,12 @@ class LutrisWindow(Gtk.ApplicationWindow, DialogLaunchUIDelegate, DialogInstallU
else:
lutris_games = {g["service_id"]: g for g in games_db.get_games(filters={"service": self.service.id})}
return [
self.combine_games(game, lutris_games.get(game["appid"]))
for game in self.apply_view_sort(service_games, lambda game: lutris_games.get(game["appid"]) or game)
if self.game_matches(game)
]
return self.filter_games(
[
self.combine_games(game, lutris_games.get(game["appid"]))
for game in self.apply_view_sort(service_games, lambda game: lutris_games.get(game["appid"]) or game)
]
)
def get_games_from_filters(self):
service_id = self.filters.get("service")
@ -474,7 +486,7 @@ class LutrisWindow(Gtk.ApplicationWindow, DialogLaunchUIDelegate, DialogInstallU
filters = self.get_sql_filters()
games = games_db.get_games(filters=filters)
games = [game for game in games if game["id"] in category_game_ids and self.game_matches(game)]
games = self.filter_games([game for game in games if game["id"] in category_game_ids])
return self.apply_view_sort(games)
def get_sql_filters(self):
@ -488,7 +500,7 @@ class LutrisWindow(Gtk.ApplicationWindow, DialogLaunchUIDelegate, DialogInstallU
sql_filters["installed"] = "1"
# We omit the "text" search here because SQLite does a fairly literal
# search, which is accent sensitive. We'll do better with self.game_matches()
# search, which is accent sensitive. We'll do better with self.filter_games()
return sql_filters
def get_service_media(self, icon_type):

View file

@ -1,10 +1,8 @@
import os
from gettext import gettext as _
from lutris.exceptions import MissingGameExecutableError
from lutris.runners.runner import Runner
from lutris.util import system
from lutris.util.log import logger
class MissingVitaTitleIDError(MissingGameExecutableError):
"""Raise when the Title ID field has not be supplied to the Vita runner game options"""
@ -30,7 +28,10 @@ class vita3k(Runner):
"type": "string",
"label": _("Title ID of Installed Application"),
"argument": "-r",
"help": _("Title ID of installed application. Eg.\"PCSG00042\". User installed apps are located in ux0:/app/<title-id>."),
"help": _(
'Title ID of installed application. Eg."PCSG00042". User installed apps are located in '
"ux0:/app/<title-id>."
),
}
]
runner_options = [
@ -47,14 +48,17 @@ class vita3k(Runner):
"type": "file",
"label": _("Config location"),
"argument": "-c",
"help": _("Get a configuration file from a given location. If a filename is given, it must end with \".yml\", otherwise it will be assumed to be a directory."),
"help": _(
'Get a configuration file from a given location. If a filename is given, it must end with ".yml", '
"otherwise it will be assumed to be a directory."
),
},
{
"option": "load-config",
"label": _("Load configuration file"),
"type": "bool",
"argument": "-f",
"help": _("If trues, informs the emualtor to load the config file from the \"Config location\" option.")
"help": _('If trues, informs the emualtor to load the config file from the "Config location" option.'),
},
]
@ -72,15 +76,15 @@ class vita3k(Runner):
continue
if option["type"] == "bool":
if self.runner_config.get(option["option"]):
if 'argument' in option:
if "argument" in option:
arguments.append(option["argument"])
elif option["type"] == "choice":
if self.runner_config.get(option["option"]) != "off":
if 'argument' in option:
if "argument" in option:
arguments.append(option["argument"])
arguments.append(config.get(option["option"]))
elif option["type"] in ("string", "file"):
if 'argument' in option:
if "argument" in option:
arguments.append(option["argument"])
arguments.append(config.get(option["option"]))
else:
@ -98,4 +102,4 @@ class vita3k(Runner):
@property
def game_path(self):
return self.game_config.get(self.entry_point_option, '')
return self.game_config.get(self.entry_point_option, "")

View file

@ -1,8 +1,7 @@
import unittest
from unittest.mock import MagicMock, patch
from lutris.runners.vita3k import MissingVitaTitleIDError
from lutris.runners.vita3k import vita3k
from lutris.runners.vita3k import MissingVitaTitleIDError, vita3k
class TestVita3kRunner(unittest.TestCase):
@ -19,7 +18,7 @@ class TestVita3kRunner(unittest.TestCase):
mock_config.game_config = {"main_file": main_file}
mock_config.runner_config = MagicMock()
self.runner.config = mock_config
with self.assertRaises(MissingVitaTitleIDError) as cm:
with self.assertRaises(MissingVitaTitleIDError):
self.runner.play()
@patch("lutris.util.system.path_exists")
@ -43,9 +42,7 @@ class TestVita3kRunner(unittest.TestCase):
mock_isfile.return_value = True
mock_config = MagicMock()
mock_config.game_config = {"main_file": main_file}
mock_config.runner_config = {
"fullscreen": False
}
mock_config.runner_config = {"fullscreen": False}
self.runner.config = mock_config
expected = {"command": [self.runner.get_executable(), "-r", main_file]}
self.assertEqual(self.runner.play(), expected)