- Fixes the problem where the path ~/.local/share/ULWGL was the first path candidate which would cause lutris to run outdated files while the updated files pulled from the runtime would remain in the runtime directory. The current model expects clients to install ULWGL as a system package (e.g., flatpak, system package) and execute it.
- To ensure the ULWGL launcher and runtime files are synchronized or updated, this makes it so lutris never executes ulwgl_run.py in ~/.local/share/ULWGL and to instead prioritize the system package install then the lutris runtime directory.
This PR no longer uses SQL to search for games, but instead does it in Python.
However, the benefit is a more forgiving search. The game names and search text are normalized to form KD, and combining characters are extra whitespace are stripped.
This means that "The Signal From Tölva" can now be found by searching for "from Tolva".
This is a bit sloppy, and a bit "Do what I mean"- but even languages which consider 'ö' a distinct letter should be okay, since they surely won't have games that differ in their names only in that one letter. At least, not often.
When sync is on, the Uninstall dialog would now actually remove the game from the Lutris.net account as well, and I don't think that is intended. It's not what we used to do, in any case.
Because all the worlds a cell-phone, everything has to be switches!
Well, maybe not that, but we use switches for many other settings very like this in the preferences, and it's odd to have one be a checkbox.
This can now trigger a full library sync when you log in, if sync is enabled, and then it will update the view when that completes. No progress spinner yet.
This is done with NotificationSource to avoid using GTK signals, which would require introducing GObjects all over.
find_executable() will now return None if the executable was not found, as this is the expected behavior in Lutris. Some callers updated to use this instead of having an except clause or a call to can_find_executable().
find_required_executable() throws MissingExecutableError in that case, which is a common case. We'll use this when the error should propagate - the more common case. The longer name is meant to be a clue that this function is special, because it won't return None.