aboutsummaryrefslogtreecommitdiff
path: root/scripts/theme-studio/default_faces.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/theme-studio/default_faces.py')
-rw-r--r--scripts/theme-studio/default_faces.py111
1 files changed, 54 insertions, 57 deletions
diff --git a/scripts/theme-studio/default_faces.py b/scripts/theme-studio/default_faces.py
index 9a0dba5e3..b9633cfa7 100644
--- a/scripts/theme-studio/default_faces.py
+++ b/scripts/theme-studio/default_faces.py
@@ -6,6 +6,8 @@ import json
import pathlib
from typing import Any
+from face_specs import FACE_ATTRS
+
class DefaultFaces:
def __init__(self, data: dict[str, Any] | None):
@@ -35,49 +37,46 @@ class DefaultFaces:
data = self.face(face, effective)
return data.get(attr + "Hex") or data.get(attr)
+ def _seed_value(self, attr: dict[str, Any], data: dict[str, Any]) -> Any:
+ """Turn a snapshot field into a model value per the attribute's kind.
+
+ The snapshot speaks a different dialect than the model: colors carry a
+ Hex variant with a name fallback; weight/slant are value-narrowed to the
+ legacy bold/italic until the snapshot refresh; underline/strike/overline
+ are truthy flags that become objects; inverse/extend coerce Emacs's "t".
+ Returns None (skip) when the attribute is unset or not seedable.
+ """
+ kind, snap = attr["kind"], attr["snapshot"]
+ if not kind:
+ return None
+ if kind == "color":
+ return data.get(snap + "Hex") or data.get(snap)
+ if kind == "weight-bold":
+ return "bold" if data.get(snap) == "bold" else None
+ if kind == "slant-italic":
+ return "italic" if data.get(snap) == "italic" else None
+ if kind == "underline-obj":
+ return {"style": "line", "color": None} if data.get(snap) else None
+ if kind == "color-obj":
+ return {"color": None} if data.get(snap) else None
+ if kind == "bool":
+ return True if data.get(snap) in (True, "t") else None
+ if kind == "scalar":
+ return data.get(snap) or None
+ if kind == "height":
+ h = data.get(snap)
+ return h if (h and h != 1) else None
+ if kind == "box":
+ return self.box_to_theme(data.get(snap))
+ return None
+
def seed(self, face: str, effective: bool = False) -> dict[str, Any]:
data = self.face(face, effective)
out: dict[str, Any] = {}
- fg = data.get("foregroundHex") or data.get("foreground")
- bg = data.get("backgroundHex") or data.get("background")
- if fg:
- out["fg"] = fg
- if bg:
- out["bg"] = bg
- # Representation-only cutover: the snapshot's bold/italic become the new
- # weight/slant shape, and underline/strike become objects. The same
- # narrowing as before (only "bold"/"italic" survive; richer weights and
- # underline colors wait for the snapshot refresh), so the emitted theme
- # is byte-identical.
- if data.get("weight") == "bold":
- out["weight"] = "bold"
- if data.get("slant") == "italic":
- out["slant"] = "italic"
- if data.get("underline"):
- out["underline"] = {"style": "line", "color": None}
- if data.get("strike"):
- out["strike"] = {"color": None}
- # Additive attrs the snapshot already carries for the faces that set them:
- # distant-foreground (e.g. lazy-highlight), inverse-video, and extend.
- # overline is captured too once the snapshot is refreshed; stock faces
- # almost never set it, so it is usually absent. These seed faces with the
- # attrs Emacs gives them by default, so the studio opens closer to reality.
- df = data.get("distantForegroundHex") or data.get("distantForeground")
- if df:
- out["distant-fg"] = df
- if data.get("overline"):
- out["overline"] = {"color": None}
- if data.get("inverseVideo") in (True, "t"):
- out["inverse"] = True
- if data.get("extend") in (True, "t"):
- out["extend"] = True
- if data.get("inherit"):
- out["inherit"] = data.get("inherit")
- if data.get("height") and data.get("height") != 1:
- out["height"] = data.get("height")
- box = self.box_to_theme(data.get("box"))
- if box:
- out["box"] = box
+ for attr in FACE_ATTRS:
+ v = self._seed_value(attr, data)
+ if v:
+ out[attr["model"]] = v
return out
def box_to_theme(self, box: Any) -> dict[str, Any] | None:
@@ -142,32 +141,30 @@ class DefaultFaces:
"packageInherits": package_inherits,
}
- def _build_color_hex(self) -> dict[str, str]:
- out: dict[str, str] = {}
+ def _iter_color_pairs(self):
+ """Yield (name, hexValue) over every face's chosen/effective color attrs.
+ Both color maps walk this same nested structure; they differ only in which
+ of the pair is the key and how each filters."""
if not self.data:
- return out
+ return
for data in self.data.get("faces", {}).values():
for block in ("chosenGuiLight", "effectiveGuiLight"):
face_data = data.get(block, {}) or {}
for attr in ("foreground", "background", "distantForeground"):
- name = face_data.get(attr)
- hex_value = face_data.get(attr + "Hex")
- if name and hex_value:
- out[str(name).lower().replace(" ", "")] = hex_value
+ yield face_data.get(attr), face_data.get(attr + "Hex")
+
+ def _build_color_hex(self) -> dict[str, str]:
+ out: dict[str, str] = {}
+ for name, hex_value in self._iter_color_pairs():
+ if name and hex_value:
+ out[str(name).lower().replace(" ", "")] = hex_value
return out
def _build_color_names(self) -> dict[str, str]:
out: dict[str, str] = {}
- if not self.data:
- return out
- for data in self.data.get("faces", {}).values():
- for block in ("chosenGuiLight", "effectiveGuiLight"):
- face_data = data.get(block, {}) or {}
- for attr in ("foreground", "background", "distantForeground"):
- hex_value = face_data.get(attr + "Hex")
- name = face_data.get(attr)
- if hex_value and name and not str(name).startswith("#"):
- out.setdefault(hex_value.lower(), str(name).lower().replace(" ", "-"))
+ for name, hex_value in self._iter_color_pairs():
+ if hex_value and name and not str(name).startswith("#"):
+ out.setdefault(hex_value.lower(), str(name).lower().replace(" ", "-"))
return out