From 4b323a6b816f93766ea7ab7173a236eb36b9a633 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 28 May 2024 14:16:25 +0530 Subject: [PATCH] Work on UI for features --- kittens/choose_fonts/face.go | 167 ++++++++++++++++++++++++++++++++++ kittens/choose_fonts/types.go | 9 +- 2 files changed, 172 insertions(+), 4 deletions(-) diff --git a/kittens/choose_fonts/face.go b/kittens/choose_fonts/face.go index 3ee5d1786..e248c86e5 100644 --- a/kittens/choose_fonts/face.go +++ b/kittens/choose_fonts/face.go @@ -280,6 +280,169 @@ func (self *face_panel) set(setting string) { } } +type ParsedFontFeature struct { + tag string + val uint + is_bool bool +} + +func (self ParsedFontFeature) String() string { + if self.is_bool { + return utils.IfElse(self.val == 0, "-", "+") + self.tag + } + return fmt.Sprintf("%s=%d", self.tag, self.val) +} + +type settable_string struct { + val string + is_set bool +} + +type FontSpec struct { + family, style, postscript_name, full_name, system, variable_name settable_string + axes map[string]float64 + features []ParsedFontFeature +} + +func (self FontSpec) String() string { + if self.system.val != "" { + return self.system.val + } + ans := strings.Builder{} + a := func(k string, v settable_string) { + if v.is_set { + ans.WriteString(fmt.Sprintf(" %s=%s", k, shlex.Quote(v.val))) + } + } + a(`family`, self.family) + a(`style`, self.style) + a(`postscript_name`, self.postscript_name) + a(`full_name`, self.full_name) + a(`variable_name`, self.variable_name) + for name, val := range self.axes { + a(name, settable_string{strconv.FormatFloat(val, 'f', -1, 64), true}) + } + if len(self.features) > 0 { + buf := strings.Builder{} + for _, f := range self.features { + buf.WriteString(f.String()) + buf.WriteString(" ") + } + a(`features`, settable_string{strings.TrimSpace(buf.String()), true}) + } + return strings.TrimSpace(ans.String()) +} + +func NewParsedFontFeature(x string) (ans ParsedFontFeature, err error) { + if x != "" { + if x[0] == '+' || x[1] == '-' { + return ParsedFontFeature{x[1:], utils.IfElse(x[0] == '+', uint(1), uint(0)), true}, nil + } else { + tag, val, found := strings.Cut(x, "=") + pff := ParsedFontFeature{tag: tag} + if found { + v, err := strconv.ParseUint(val, 10, 0) + if err != nil { + return ans, err + } + pff.val = uint(v) + } + return pff, nil + } + } + return +} + +func NewFontSpec(spec string) (ans FontSpec, err error) { + if spec == "" || spec == "auto" { + ans.system = settable_string{"auto", true} + return + } + parts, err := shlex.Split(spec) + if err != nil { + return + } + if !strings.Contains(parts[0], "=") { + ans.system = settable_string{spec, true} + return + } + for _, item := range parts { + k, v, found := strings.Cut(item, "=") + if !found { + return ans, fmt.Errorf(fmt.Sprintf("The font specification %s is invalid as %s does not contain an =", spec, item)) + } + switch k { + case "family": + ans.family = settable_string{v, true} + case "style": + ans.style = settable_string{v, true} + case "full_name": + ans.full_name = settable_string{v, true} + case "postscript_name": + ans.postscript_name = settable_string{v, true} + case "variable_name": + ans.variable_name = settable_string{v, true} + case "features": + for _, x := range utils.NewSeparatorScanner(v, " ").Split(v) { + pff, err := NewParsedFontFeature(x) + if err != nil { + return ans, err + } + ans.features = append(ans.features, pff) + } + default: + f, err := strconv.ParseFloat(v, 64) + if err != nil { + return ans, err + } + ans.axes[k] = f + } + } + return +} + +func (self *face_panel) update_feature_in_setting(value string) error { + fs, err := NewFontSpec(self.get()) + if err != nil { + return err + } + pff, err := NewParsedFontFeature(value) + if err != nil { + return err + } + found := false + for _, f := range fs.features { + if f.tag == pff.tag { + f.val = pff.val + found = true + break + } + } + if !found { + fs.features = append(fs.features, pff) + } + self.set(fs.String()) + return nil +} + +func (self *face_panel) handle_click_on_feature(feat_tag string) error { + d := self.current_preview.Features[feat_tag] + if d.Is_index { + } else { + for q, serialized := range self.current_preview.Applied_features { + if q == feat_tag { + if serialized != "" && (serialized[0] == '+' || strings.HasSuffix(serialized, "=1")) { + return self.update_feature_in_setting("-" + feat_tag) + } else { + return self.update_feature_in_setting("+" + feat_tag) + } + } + } + return self.update_feature_in_setting("+" + feat_tag) + } + return nil +} + func (self *face_panel) on_click(id string) (err error) { scheme, val, _ := strings.Cut(id, ":") switch scheme { @@ -287,6 +450,10 @@ func (self *face_panel) on_click(id string) (err error) { self.set(fmt.Sprintf(`family="%s" style="%s"`, self.family, val)) case "variable_style": self.set(self.variable_spec(val, nil)) + case "feature": + if err = self.handle_click_on_feature(val); err != nil { + return err + } case "axis": p, tag, _ := strings.Cut(val, ":") num, den, _ := strings.Cut(p, "/") diff --git a/kittens/choose_fonts/types.go b/kittens/choose_fonts/types.go index cb08979d6..35e8d3e19 100644 --- a/kittens/choose_fonts/types.go +++ b/kittens/choose_fonts/types.go @@ -88,10 +88,11 @@ type ListResult struct { } type FeatureData struct { - Name string `json:"name"` - Tooltip string `json:"tooltip"` - Sample string `json:"sample"` - Params []string `json:"params"` + Is_index bool `json:"is_index"` + Name string `json:"name"` + Tooltip string `json:"tooltip"` + Sample string `json:"sample"` + Params []string `json:"params"` } type RenderedSampleTransmit struct {