mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-17 22:14:53 +02:00
Implement toggling of boolean features
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -280,133 +281,8 @@ func (self *face_panel) set(setting string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParsedFontFeature struct {
|
func (self *face_panel) update_feature_in_setting(pff ParsedFontFeature) error {
|
||||||
tag string
|
fs, err := NewFontSpec(self.get(), self.current_preview.Features)
|
||||||
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -419,7 +295,21 @@ func (self *face_panel) update_feature_in_setting(value string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
fs.features = append(fs.features, pff)
|
fs.features = append(fs.features, &pff)
|
||||||
|
}
|
||||||
|
self.set(fs.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *face_panel) remove_feature_in_setting(tag string) error {
|
||||||
|
fs, err := NewFontSpec(self.get(), self.current_preview.Features)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(fs.features) > 0 {
|
||||||
|
fs.features = slices.DeleteFunc(fs.features, func(x *ParsedFontFeature) bool {
|
||||||
|
return x.tag == tag
|
||||||
|
})
|
||||||
}
|
}
|
||||||
self.set(fs.String())
|
self.set(fs.String())
|
||||||
return nil
|
return nil
|
||||||
@@ -431,14 +321,16 @@ func (self *face_panel) handle_click_on_feature(feat_tag string) error {
|
|||||||
} else {
|
} else {
|
||||||
for q, serialized := range self.current_preview.Applied_features {
|
for q, serialized := range self.current_preview.Applied_features {
|
||||||
if q == feat_tag {
|
if q == feat_tag {
|
||||||
if serialized != "" && (serialized[0] == '+' || strings.HasSuffix(serialized, "=1")) {
|
if serialized == "" {
|
||||||
return self.update_feature_in_setting("-" + feat_tag)
|
continue
|
||||||
} else {
|
|
||||||
return self.update_feature_in_setting("+" + feat_tag)
|
|
||||||
}
|
}
|
||||||
|
if serialized[0] == '-' {
|
||||||
|
return self.remove_feature_in_setting(feat_tag)
|
||||||
|
}
|
||||||
|
return self.update_feature_in_setting(ParsedFontFeature{tag: feat_tag, is_bool: true, val: 0})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self.update_feature_in_setting("+" + feat_tag)
|
return self.update_feature_in_setting(ParsedFontFeature{tag: feat_tag, is_bool: true, val: 1})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,12 @@ package choose_fonts
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"kitty/tools/utils"
|
||||||
|
"kitty/tools/utils/shlex"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
@@ -196,3 +201,128 @@ func has_variable_data_for_font(font ListedFont) bool {
|
|||||||
_, found := _cached_vd(font.cache_key())
|
_, found := _cached_vd(font.cache_key())
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, features map[string]FeatureData) (ans ParsedFontFeature, err error) {
|
||||||
|
if x != "" {
|
||||||
|
if x[0] == '+' || x[0] == '-' {
|
||||||
|
return ParsedFontFeature{x[1:], utils.IfElse(x[0] == '+', uint(1), uint(0)), true}, nil
|
||||||
|
} else {
|
||||||
|
tag, val, found := strings.Cut(x, "=")
|
||||||
|
fd, defn_found := features[tag]
|
||||||
|
if defn_found && !fd.Is_index {
|
||||||
|
return ParsedFontFeature{tag, 1, true}, nil
|
||||||
|
}
|
||||||
|
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, features map[string]FeatureData) (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, features)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user