diff --git a/lutris/gui/installer/widgets.py b/lutris/gui/installer/widgets.py
index f56db70dc..a34fa5cec 100644
--- a/lutris/gui/installer/widgets.py
+++ b/lutris/gui/installer/widgets.py
@@ -1,4 +1,6 @@
-from gi.repository import GLib, Gtk, Pango
+from gi.repository import Gtk, Pango
+
+from lutris.util.strings import is_valid_pango_markup
class InstallerLabel(Gtk.Label):
@@ -7,8 +9,6 @@ class InstallerLabel(Gtk.Label):
def __init__(self, text, wrap=True, selectable=False):
super().__init__()
- is_valid_markup = InstallerLabel.is_valid_pango_markup(text)
-
if wrap:
self.set_line_wrap(True)
self.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR)
@@ -17,7 +17,7 @@ class InstallerLabel(Gtk.Label):
self.set_alignment(0, 0.5)
self.set_margin_right(12)
- if is_valid_markup:
+ if is_valid_pango_markup(text):
self.set_markup(text)
else:
self.set_text(text)
@@ -25,21 +25,3 @@ class InstallerLabel(Gtk.Label):
self.props.can_focus = False
self.set_tooltip_text(text)
self.set_selectable(selectable)
-
- @staticmethod
- def is_valid_pango_markup(text):
- def destroy_func(_user_data):
- pass # required by GLib, but we don't need this callback
-
- if len(text) == 0:
- return True # Trivial case - empty strings are always valid
-
- try:
- parser = GLib.MarkupParser()
- context = GLib.MarkupParseContext(parser, GLib.MarkupParseFlags.DEFAULT_FLAGS, None, destroy_func)
-
- markup = f"{text}"
- context.parse(markup, len(markup))
- return True
- except GLib.GError:
- return False
diff --git a/lutris/util/strings.py b/lutris/util/strings.py
index da04f5e5f..58b91fdec 100644
--- a/lutris/util/strings.py
+++ b/lutris/util/strings.py
@@ -7,6 +7,8 @@ import uuid
from gettext import gettext as _
from typing import List, Union
+from gi.repository import GLib
+
from lutris.util.log import logger
NO_PLAYTIME = "Never played"
@@ -101,12 +103,31 @@ def unpack_dependencies(string: str) -> List[Union[str, tuple]]:
return [dep for dep in [_expand_dep(dep) for dep in string.split(",")] if dep]
-def gtk_safe(string: str) -> str:
- """Return a string ready to used in Gtk widgets"""
- if not string:
- string = ""
- string = str(string)
- return string.replace("&", "&").replace("<", "<").replace(">", ">")
+def gtk_safe(text: str) -> str:
+ """Return a string ready to used in Gtk widgets, with anything that could
+ be Pango markup escaped."""
+ if not text:
+ return ""
+
+ return GLib.markup_escape_text(str(text))
+
+
+def is_valid_pango_markup(text):
+ def destroy_func(_user_data):
+ pass # required by GLib, but we don't need this callback
+
+ if len(text) == 0:
+ return True # Trivial case - empty strings are always valid
+
+ try:
+ parser = GLib.MarkupParser()
+ context = GLib.MarkupParseContext(parser, GLib.MarkupParseFlags.DEFAULT_FLAGS, None, destroy_func)
+
+ markup = f"{text}"
+ context.parse(markup, len(markup))
+ return True
+ except GLib.GError:
+ return False
def get_formatted_playtime(playtime) -> str: