mirror of
https://github.com/lutris/lutris
synced 2024-10-14 19:53:53 +00:00
Restore the playtime widget.
The text is now formatted as Lutris does in the game bar, and I've added a function to parse this. It's a bit picky, but should tolerate localization at least. Resolves #5151
This commit is contained in:
parent
32841b9ab6
commit
818ee07567
|
@ -15,14 +15,14 @@ from lutris.gui.config import DIALOG_HEIGHT, DIALOG_WIDTH
|
|||
from lutris.gui.config.boxes import GameBox, RunnerBox, SystemConfigBox, UnderslungMessageBox
|
||||
from lutris.gui.dialogs import DirectoryDialog, ErrorDialog, QuestionDialog, SavableModelessDialog
|
||||
from lutris.gui.dialogs.delegates import DialogInstallUIDelegate
|
||||
from lutris.gui.widgets.common import FloatEntry, Label, NumberEntry, SlugEntry
|
||||
from lutris.gui.widgets.common import Label, NumberEntry, SlugEntry
|
||||
from lutris.gui.widgets.notifications import send_notification
|
||||
from lutris.gui.widgets.scaled_image import ScaledImage
|
||||
from lutris.gui.widgets.utils import get_image_file_format, invalidate_media_caches
|
||||
from lutris.runners import import_runner
|
||||
from lutris.services.lutris import LutrisBanner, LutrisCoverart, LutrisIcon, download_lutris_media
|
||||
from lutris.util.log import logger
|
||||
from lutris.util.strings import gtk_safe, slugify
|
||||
from lutris.util.strings import gtk_safe, parse_playtime, slugify
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes, no-member
|
||||
|
@ -155,8 +155,7 @@ class GameDialogCommon(SavableModelessDialog, DialogInstallUIDelegate):
|
|||
|
||||
info_box.pack_start(self._get_year_box(), False, False, 6) # Year
|
||||
|
||||
# This looks atrocious. Fix it.
|
||||
# info_box.pack_start(self._get_playtime_box(), False, False, 6) # Playtime
|
||||
info_box.pack_start(self._get_playtime_box(), False, False, 6) # Playtime
|
||||
|
||||
if self.game:
|
||||
info_box.pack_start(self._get_slug_box(), False, False, 6)
|
||||
|
@ -326,13 +325,12 @@ class GameDialogCommon(SavableModelessDialog, DialogInstallUIDelegate):
|
|||
def _get_playtime_box(self):
|
||||
box = Gtk.Box(spacing=12, margin_right=12, margin_left=12)
|
||||
|
||||
label = Label(_("Playtime (in hours)"))
|
||||
label = Label(_("Playtime"))
|
||||
box.pack_start(label, False, False, 0)
|
||||
self.playtime_entry = FloatEntry()
|
||||
self.playtime_entry.set_max_length(10)
|
||||
self.playtime_entry = Gtk.Entry()
|
||||
|
||||
if self.game:
|
||||
self.playtime_entry.set_text(f"{self.game.playtime}")
|
||||
self.playtime_entry.set_text(self.game.formatted_playtime)
|
||||
box.pack_start(self.playtime_entry, True, True, 0)
|
||||
|
||||
return box
|
||||
|
@ -616,12 +614,13 @@ class GameDialogCommon(SavableModelessDialog, DialogInstallUIDelegate):
|
|||
if self.runner_name == "steam" and not self.lutris_config.game_config.get("appid"):
|
||||
ErrorDialog(_("Steam AppID not provided"), parent=self)
|
||||
return False
|
||||
# if self.playtime_entry.get_text():
|
||||
# try:
|
||||
# float(self.playtime_entry.get_text())
|
||||
# except ValueError:
|
||||
# ErrorDialog(_("The entered playtime is invalid"), parent=self)
|
||||
# return False
|
||||
playtime_text = self.playtime_entry.get_text()
|
||||
if playtime_text and playtime_text != self.game.formatted_playtime:
|
||||
try:
|
||||
parse_playtime(playtime_text)
|
||||
except ValueError as ex:
|
||||
ErrorDialog(ex, parent=self)
|
||||
return False
|
||||
|
||||
invalid_fields = []
|
||||
runner_class = import_runner(self.runner_name)
|
||||
|
@ -662,9 +661,10 @@ class GameDialogCommon(SavableModelessDialog, DialogInstallUIDelegate):
|
|||
if self.year_entry.get_text():
|
||||
year = int(self.year_entry.get_text())
|
||||
|
||||
# playtime = None
|
||||
# if self.playtime_entry.get_text():
|
||||
# playtime = float(self.playtime_entry.get_text())
|
||||
playtime = None
|
||||
playtime_text = self.playtime_entry.get_text()
|
||||
if playtime_text and playtime_text != self.game.formatted_playtime:
|
||||
playtime = parse_playtime(playtime_text)
|
||||
|
||||
if not self.lutris_config.game_config_id:
|
||||
self.lutris_config.game_config_id = make_game_config_id(self.slug)
|
||||
|
@ -673,7 +673,8 @@ class GameDialogCommon(SavableModelessDialog, DialogInstallUIDelegate):
|
|||
self.game.sortname = sortname
|
||||
self.game.slug = self.slug
|
||||
self.game.year = year
|
||||
# self.game.playtime = playtime
|
||||
if playtime:
|
||||
self.game.playtime = playtime
|
||||
self.game.is_installed = True
|
||||
self.game.config = self.lutris_config
|
||||
self.game.runner_name = self.runner_name
|
||||
|
|
|
@ -35,19 +35,6 @@ class NumberEntry(Gtk.Entry, Gtk.Editable):
|
|||
return position
|
||||
|
||||
|
||||
class FloatEntry(Gtk.Entry, Gtk.Editable):
|
||||
|
||||
def do_insert_text(self, new_text, length, position):
|
||||
"""Filter inserted characters to only accept floating-point numbers"""
|
||||
decimal_count = self.get_buffer().get_text().count(".")
|
||||
new_text = "".join([c for c in new_text if c.isnumeric() or (c == "." and decimal_count < 1)])
|
||||
if new_text:
|
||||
length = len(new_text)
|
||||
self.get_buffer().insert_text(position, new_text, length)
|
||||
return position + length
|
||||
return position
|
||||
|
||||
|
||||
class FileChooserEntry(Gtk.Box):
|
||||
"""Editable entry with a file picker button"""
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ from gi.repository import GLib
|
|||
|
||||
from lutris.util.log import logger
|
||||
|
||||
NO_PLAYTIME = "Never played"
|
||||
NO_PLAYTIME = _("Never played")
|
||||
|
||||
|
||||
def get_uuid_from_string(value: str) -> str:
|
||||
|
@ -161,7 +161,7 @@ def get_formatted_playtime(playtime: float) -> str:
|
|||
else:
|
||||
hours_text = ""
|
||||
|
||||
minutes = int((playtime - hours) * 60)
|
||||
minutes = int(round((playtime - hours) * 60, 0))
|
||||
if minutes == 1:
|
||||
minutes_text = _("1 minute")
|
||||
elif minutes > 1:
|
||||
|
@ -177,6 +177,56 @@ def get_formatted_playtime(playtime: float) -> str:
|
|||
return NO_PLAYTIME
|
||||
|
||||
|
||||
def parse_playtime(text: str) -> float:
|
||||
"""Parses a textual playtime, as produced by get_formatted_playtime(), back into
|
||||
a number, a count of hours (with fractions)."""
|
||||
text = text.strip().casefold()
|
||||
|
||||
if _("Less than a minute").casefold() == text:
|
||||
return 0.0
|
||||
|
||||
if NO_PLAYTIME.casefold() == text:
|
||||
return 0.0
|
||||
|
||||
playtime = 0.0
|
||||
error_message = _("'%s' is not a valid playtime.") % text
|
||||
|
||||
def find_hours(num: float, unit: str) -> float:
|
||||
# This works by reformatted the number and unit and then
|
||||
# comparing to the localized text we would have produced from
|
||||
# formatting; in this way we can recognize the units.
|
||||
normalized = "%d %s" % (num, unit)
|
||||
if normalized == _("1 minute").casefold():
|
||||
return 1 / 60
|
||||
if normalized == (_("%d minutes") % num).casefold():
|
||||
return num / 60
|
||||
if normalized == _("1 hour").casefold():
|
||||
return 1
|
||||
if normalized == (_("%d hours") % num).casefold():
|
||||
return num
|
||||
raise ValueError(error_message)
|
||||
|
||||
parts = iter(text.split())
|
||||
try:
|
||||
while True:
|
||||
num_text = next(parts)
|
||||
try:
|
||||
num = float(num_text)
|
||||
except ValueError as ex:
|
||||
raise ValueError(error_message) from ex
|
||||
|
||||
try:
|
||||
unit = next(parts)
|
||||
except StopIteration as ex:
|
||||
raise ValueError(error_message) from ex
|
||||
|
||||
playtime += find_hours(num, unit)
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
return playtime
|
||||
|
||||
|
||||
def _split_arguments(args: str, closing_quot: str = '', quotations: str = None) -> List[str]:
|
||||
if quotations is None:
|
||||
quotations = ["'", '"']
|
||||
|
|
Loading…
Reference in a new issue