From 034f0b1af1b7954de425581f07a99d1b2d3a77b3 Mon Sep 17 00:00:00 2001 From: Luca Toniolo <10792599+grandixximo@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:35:45 +0800 Subject: [PATCH 1/3] touchy: replace deprecated font and colour overrides with CSS GTK deprecated Gtk.Widget.override_font() and modify_fg() in 3.16. Touchy used them throughout setfont() to set every display font and the per-coordinate DRO text colours, logging deprecation warnings. Tag the widgets with style classes once and drive the fonts and colours from a single GtkCssProvider on the screen, rebuilt whenever a font or colour preference changes. A Pango font description is translated to CSS font properties and stored colours are normalised through Gdk.RGBA. The rendered result is unchanged. The modify_bg() calls in filechooser, listing and mdi are a separate selection-highlight pattern and are left for a follow-up. --- src/emc/usr_intf/touchy/touchy.py | 88 ++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/src/emc/usr_intf/touchy/touchy.py b/src/emc/usr_intf/touchy/touchy.py index 80d26bc0465..60246e4c72e 100755 --- a/src/emc/usr_intf/touchy/touchy.py +++ b/src/emc/usr_intf/touchy/touchy.py @@ -951,6 +951,16 @@ def _scale_fonts(self, factor): self.setfont() def setfont(self): + # GTK 3.16 deprecated the per-widget override_font/modify_fg + # calls, so tag the widgets with style classes once and drive + # their fonts and DRO text colours from a CSS provider that is + # reloaded whenever a font or colour preference changes. + if not hasattr(self, "_css_provider"): + self._css_provider = Gtk.CssProvider() + Gtk.StyleContext.add_provider_for_screen( + Gdk.Screen.get_default(), self._css_provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + # buttons for i in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "minus", "decimal", @@ -972,26 +982,26 @@ def setfont(self): "fitfontscheck"]: w = self.wTree.get_object(i) if w: - w.override_font(self.control_font) + self._tag(w, "touchy_control") notebook = self.wTree.get_object('notebook1') for i in range(notebook.get_n_pages()): w = notebook.get_nth_page(i) - notebook.get_tab_label(w).override_font(self.control_font) + self._tag(notebook.get_tab_label(w), "touchy_control") # labels for i in range(self.num_mdi_labels): w = self.wTree.get_object("mdi%d" % i) - w.override_font(self.control_font) + self._tag(w, "touchy_control") for i in range(self.num_filechooser_labels): w = self.wTree.get_object("filechooser%d" % i) - w.override_font(self.control_font) + self._tag(w, "touchy_control") for i in range(self.num_listing_labels): w = self.wTree.get_object("listing%d" % i) - w.override_font(self.listing_font) + self._tag(w, "touchy_listing") for i in ["mdi", "startup", "manual", "auto", "preferences", "status", "relative", "absolute", "dtg", "ss2label", "status_spindlespeed2"]: w = self.wTree.get_object(i) - w.override_font(self.control_font) + self._tag(w, "touchy_control") # dro for i in ['xr', 'yr', 'zr', 'ar', 'br', 'cr', 'ur', 'vr', 'wr', @@ -999,20 +1009,74 @@ def setfont(self): 'xd', 'yd', 'zd', 'ad', 'bd', 'cd', 'ud', 'vd', 'wd']: w = self.wTree.get_object(i) if w: - w.override_font(self.dro_font) + self._tag(w, "touchy_dro") if "r" in i and not self.rel_textcolor == "default": - w.modify_fg(Gtk.StateFlags.NORMAL,Gdk.color_parse(self.rel_textcolor)) + self._tag(w, "touchy_rel") elif "a" in i and not self.abs_textcolor == "default": - w.modify_fg(Gtk.StateFlags.NORMAL,Gdk.color_parse(self.abs_textcolor)) + self._tag(w, "touchy_abs") elif "d" in i and not self.dtg_textcolor == "default": - w.modify_fg(Gtk.StateFlags.NORMAL,Gdk.color_parse(self.dtg_textcolor)) + self._tag(w, "touchy_dtg") # status bar for i in ["error"]: w = self.wTree.get_object(i) - w.override_font(self.error_font) + self._tag(w, "touchy_error") if not self.err_textcolor == "default": - w.modify_fg(Gtk.StateFlags.NORMAL,Gdk.color_parse(self.err_textcolor)) + self._tag(w, "touchy_err") + + self._reload_font_css() + + def _tag(self, widget, name): + # Add a style class once; the shared CSS provider carries the font + # and colour for that class. + if widget is not None: + context = widget.get_style_context() + if not context.has_class(name): + context.add_class(name) + + def _font_to_css(self, fd): + # Translate a Pango font description into CSS declarations. + css = [] + family = fd.get_family() + if family: + css.append('font-family: "%s";' % family) + if fd.get_size() > 0: + unit = "px" if fd.get_size_is_absolute() else "pt" + css.append("font-size: %d%s;" % (fd.get_size() // Pango.SCALE, unit)) + weight = max(100, min(900, int(round(int(fd.get_weight()) / 100.0)) * 100)) + css.append("font-weight: %d;" % weight) + if fd.get_style() == Pango.Style.ITALIC: + css.append("font-style: italic;") + elif fd.get_style() == Pango.Style.OBLIQUE: + css.append("font-style: oblique;") + return " ".join(css) + + def _css_color(self, value): + # Normalise a stored colour string to a CSS-safe form, or None. + rgba = Gdk.RGBA() + if rgba.parse(value): + return rgba.to_string() + return None + + def _reload_font_css(self): + rules = [ + ".touchy_control { %s }" % self._font_to_css(self.control_font), + ".touchy_listing { %s }" % self._font_to_css(self.listing_font), + ".touchy_dro { %s }" % self._font_to_css(self.dro_font), + ".touchy_error { %s }" % self._font_to_css(self.error_font), + ] + for name, value in (("touchy_rel", self.rel_textcolor), + ("touchy_abs", self.abs_textcolor), + ("touchy_dtg", self.dtg_textcolor), + ("touchy_err", self.err_textcolor)): + if value != "default": + colour = self._css_color(value) + if colour: + rules.append(".%s { color: %s; }" % (name, colour)) + try: + self._css_provider.load_from_data("\n".join(rules).encode("utf-8")) + except Exception: + pass def mdi_set_tool(self, b): self.mdi_control.set_tool(self.status.get_current_tool(), self.g10l11) From 901c82167a3d8ea956eef671a2f98cbf8f2a0a55 Mon Sep 17 00:00:00 2001 From: Luca Toniolo <10792599+grandixximo@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:13:09 +0800 Subject: [PATCH 2/3] touchy: fix invalid default DRO font that triggers a CSS warning The default dro_font "FreeMono 10 Pitch Bold 16" parses to the Pango family "FreeMono 10 Pitch". The preference font buttons use use-font=True, so GtkFontButton renders its label in that font and emits the family as unquoted CSS, which GTK rejects with "Junk at end of value for font-family" (the parse warning in #4068). "FreeMono 10 Pitch" is not a real family (Pango already fell back to monospace), so drop the legacy "10 Pitch" token and default to "FreeMono Bold 16". On a fresh profile this clears the warning. --- src/emc/usr_intf/touchy/touchy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emc/usr_intf/touchy/touchy.py b/src/emc/usr_intf/touchy/touchy.py index 60246e4c72e..e008dd7a796 100755 --- a/src/emc/usr_intf/touchy/touchy.py +++ b/src/emc/usr_intf/touchy/touchy.py @@ -141,7 +141,7 @@ def __init__(self, inifile): self.prefs = preferences.preferences() self.mv_val = self.prefs.getpref('maxvel', 100, int) self.control_font_name = self.prefs.getpref('control_font', 'Sans 18', str) - self.dro_font_name = self.prefs.getpref('dro_font', 'FreeMono 10 Pitch Bold 16', str) + self.dro_font_name = self.prefs.getpref('dro_font', 'FreeMono Bold 16', str) self.error_font_name = self.prefs.getpref('error_font', 'Sans Bold 10', str) self.listing_font_name = self.prefs.getpref('listing_font', 'Sans 10', str) self.theme_name = self.prefs.getpref('gtk_theme', 'Follow System Theme', str) From aada1178e8aec915c4364e89cec5088e59f12b31 Mon Sep 17 00:00:00 2001 From: Luca Toniolo <10792599+grandixximo@users.noreply.github.com> Date: Sun, 14 Jun 2026 09:08:42 +0800 Subject: [PATCH 3/3] touchy: rename _tag to _add_style_class Per review on #4133: a more descriptive name for the helper that adds a style class once. No behavior change. --- src/emc/usr_intf/touchy/touchy.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/emc/usr_intf/touchy/touchy.py b/src/emc/usr_intf/touchy/touchy.py index e008dd7a796..bd36c0b3d61 100755 --- a/src/emc/usr_intf/touchy/touchy.py +++ b/src/emc/usr_intf/touchy/touchy.py @@ -982,26 +982,26 @@ def setfont(self): "fitfontscheck"]: w = self.wTree.get_object(i) if w: - self._tag(w, "touchy_control") + self._add_style_class(w, "touchy_control") notebook = self.wTree.get_object('notebook1') for i in range(notebook.get_n_pages()): w = notebook.get_nth_page(i) - self._tag(notebook.get_tab_label(w), "touchy_control") + self._add_style_class(notebook.get_tab_label(w), "touchy_control") # labels for i in range(self.num_mdi_labels): w = self.wTree.get_object("mdi%d" % i) - self._tag(w, "touchy_control") + self._add_style_class(w, "touchy_control") for i in range(self.num_filechooser_labels): w = self.wTree.get_object("filechooser%d" % i) - self._tag(w, "touchy_control") + self._add_style_class(w, "touchy_control") for i in range(self.num_listing_labels): w = self.wTree.get_object("listing%d" % i) - self._tag(w, "touchy_listing") + self._add_style_class(w, "touchy_listing") for i in ["mdi", "startup", "manual", "auto", "preferences", "status", "relative", "absolute", "dtg", "ss2label", "status_spindlespeed2"]: w = self.wTree.get_object(i) - self._tag(w, "touchy_control") + self._add_style_class(w, "touchy_control") # dro for i in ['xr', 'yr', 'zr', 'ar', 'br', 'cr', 'ur', 'vr', 'wr', @@ -1009,24 +1009,24 @@ def setfont(self): 'xd', 'yd', 'zd', 'ad', 'bd', 'cd', 'ud', 'vd', 'wd']: w = self.wTree.get_object(i) if w: - self._tag(w, "touchy_dro") + self._add_style_class(w, "touchy_dro") if "r" in i and not self.rel_textcolor == "default": - self._tag(w, "touchy_rel") + self._add_style_class(w, "touchy_rel") elif "a" in i and not self.abs_textcolor == "default": - self._tag(w, "touchy_abs") + self._add_style_class(w, "touchy_abs") elif "d" in i and not self.dtg_textcolor == "default": - self._tag(w, "touchy_dtg") + self._add_style_class(w, "touchy_dtg") # status bar for i in ["error"]: w = self.wTree.get_object(i) - self._tag(w, "touchy_error") + self._add_style_class(w, "touchy_error") if not self.err_textcolor == "default": - self._tag(w, "touchy_err") + self._add_style_class(w, "touchy_err") self._reload_font_css() - def _tag(self, widget, name): + def _add_style_class(self, widget, name): # Add a style class once; the shared CSS provider carries the font # and colour for that class. if widget is not None: