text: rename Face to Family and let Face denote a family configuration

While here, rename Family.Path to Shape which a more precise term.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-10-05 19:00:11 +02:00
parent 404a065a0e
commit 0b637f549d
4 changed files with 81 additions and 54 deletions
+9 -5
View File
@@ -23,10 +23,13 @@ import (
// Editor implements an editable and scrollable text area.
type Editor struct {
// Face defines the font and style of the text.
// Family defines the font and style of the text.
Family Family
// Face specifies the font family configuration.
Face Face
// Size is the text size. If zero, a reasonable default is used.
Size unit.Value
// Size is the text size. If zero, a default size is used.
Size unit.Value
Alignment Alignment
// SingleLine force the text to stay on a single line.
// SingleLine also sets the scrolling direction to
@@ -241,7 +244,7 @@ func (e *Editor) Layout(gtx *layout.Context) {
var stack op.StackOp
stack.Push(gtx.Ops)
op.TransformOp{}.Offset(lineOff).Add(gtx.Ops)
e.Face.Path(tsize, str).Add(gtx.Ops)
e.Family.Shape(e.Face, tsize, str).Add(gtx.Ops)
paint.PaintOp{Rect: toRectF(clip).Sub(lineOff)}.Add(gtx.Ops)
stack.Pop()
}
@@ -369,7 +372,8 @@ func (e *Editor) layoutText(c unit.Converter) {
s = e.Hint
}
tsize := textSize(c, e.Size)
textLayout := e.Face.Layout(tsize, s, LayoutOptions{SingleLine: e.SingleLine, MaxWidth: e.maxWidth})
opts := LayoutOptions{SingleLine: e.SingleLine, MaxWidth: e.maxWidth}
textLayout := e.Family.Layout(e.Face, tsize, s, opts)
lines := textLayout.Lines
dims := linesDimens(lines)
for i := 0; i < len(lines)-1; i++ {
+5 -4
View File
@@ -19,10 +19,11 @@ import (
// Label is a widget for laying out and drawing text.
type Label struct {
// Face defines the font and style of the text.
// Face defines the style of the text.
Face Face
// Size is the text size. If zero, a default size is used.
Size unit.Value
// Material is a macro recording the material to draw the
// text. Use a ColorOp for colored text.
Material op.MacroOp
@@ -94,10 +95,10 @@ func (l *lineIterator) Next() (String, f32.Point, bool) {
return String{}, f32.Point{}, false
}
func (l Label) Layout(gtx *layout.Context) {
func (l Label) Layout(gtx *layout.Context, family Family) {
cs := gtx.Constraints
tsize := textSize(gtx, l.Size)
textLayout := l.Face.Layout(tsize, l.Text, LayoutOptions{MaxWidth: cs.Width.Max})
textLayout := family.Layout(l.Face, tsize, l.Text, LayoutOptions{MaxWidth: cs.Width.Max})
lines := textLayout.Lines
if max := l.MaxLines; max > 0 && len(lines) > max {
lines = lines[:max]
@@ -124,7 +125,7 @@ func (l Label) Layout(gtx *layout.Context) {
var stack op.StackOp
stack.Push(gtx.Ops)
op.TransformOp{}.Offset(off).Add(gtx.Ops)
l.Face.Path(tsize, str).Add(gtx.Ops)
family.Shape(l.Face, tsize, str).Add(gtx.Ops)
// Set a default color in case the material is empty.
paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(gtx.Ops)
l.Material.Add(gtx.Ops)
+28 -3
View File
@@ -45,12 +45,27 @@ type LayoutOptions struct {
SingleLine bool
}
type Face interface {
// Face specify a particular configuration of a Family.
type Face struct {
// Weight is the text weight. If zero, Normal is used instead.
Weight Weight
Style Style
}
// Style is the font style.
type Style int
// Weight is a font weight, in CSS units.
type Weight int
// Family implements a font family. It can layout and shape text from
// a Face and size.
type Family interface {
// Layout returns the text layout for a string given a set of
// options.
Layout(size float32, s string, opts LayoutOptions) *Layout
Layout(face Face, size float32, s string, opts LayoutOptions) *Layout
// Path returns the ClipOp outline of a text recorded in a macro.
Path(size float32, s String) op.MacroOp
Shape(face Face, size float32, s String) op.MacroOp
}
type Alignment uint8
@@ -61,6 +76,16 @@ const (
Middle
)
const (
Regular Style = iota
Italic
)
const (
Normal Weight = 400
Bold Weight = 700
)
func linesDimens(lines []Line) layout.Dimensions {
var width fixed.Int26_6
var h int
+39 -42
View File
@@ -19,9 +19,14 @@ import (
"golang.org/x/image/math/fixed"
)
// Faces is a cache of text layouts and paths.
type Faces struct {
faceCache map[faceKey]*Face
// Family is an implementation of text.Family. It caches
// layouts and paths.
// A Family must specify at least the Regular font to be useful.
type Family struct {
Regular *sfnt.Font
Italic *sfnt.Font
Bold *sfnt.Font
layoutCache map[layoutKey]cachedLayout
pathCache map[pathKey]cachedPath
}
@@ -49,20 +54,9 @@ type pathKey struct {
str string
}
type faceKey struct {
font *sfnt.Font
}
// Face is a cached implementation of text.Face.
type Face struct {
faces *Faces
font *opentype
}
// Reset the cache, discarding any measures or paths that
// Reset the cache, discarding any layouts or paths that
// haven't been used since the last call to Reset.
func (f *Faces) Reset() {
f.init()
func (f *Family) Reset() {
for pk, p := range f.pathCache {
if !p.active {
delete(f.pathCache, pk)
@@ -81,62 +75,65 @@ func (f *Faces) Reset() {
}
}
// For returns a Face for the given font.
func (f *Faces) For(fnt *sfnt.Font) *Face {
f.init()
fk := faceKey{fnt}
if f, exist := f.faceCache[fk]; exist {
return f
// for returns a font for the given face.
func (f *Family) fontFor(face text.Face) *sfnt.Font {
var font *sfnt.Font
switch {
case face.Style == text.Italic:
font = f.Italic
case face.Weight >= 600:
font = f.Bold
}
face := &Face{
faces: f,
font: &opentype{Font: fnt, Hinting: font.HintingFull},
if font == nil {
font = f.Regular
}
f.faceCache[fk] = face
return face
return font
}
func (f *Faces) init() {
if f.faceCache != nil {
func (f *Family) init() {
if f.pathCache != nil {
return
}
f.faceCache = make(map[faceKey]*Face)
f.pathCache = make(map[pathKey]cachedPath)
f.layoutCache = make(map[layoutKey]cachedLayout)
}
func (f *Face) Layout(size float32, str string, opts text.LayoutOptions) *text.Layout {
func (f *Family) Layout(face text.Face, size float32, str string, opts text.LayoutOptions) *text.Layout {
f.init()
fnt := f.fontFor(face)
ppem := fixed.Int26_6(size * 64)
lk := layoutKey{
f: f.font.Font,
f: fnt,
ppem: ppem,
str: str,
opts: opts,
}
if l, ok := f.faces.layoutCache[lk]; ok {
if l, ok := f.layoutCache[lk]; ok {
l.active = true
f.faces.layoutCache[lk] = l
f.layoutCache[lk] = l
return l.layout
}
l := layoutText(ppem, str, f.font, opts)
f.faces.layoutCache[lk] = cachedLayout{active: true, layout: l}
l := layoutText(ppem, str, &opentype{Font: fnt, Hinting: font.HintingFull}, opts)
f.layoutCache[lk] = cachedLayout{active: true, layout: l}
return l
}
func (f *Face) Path(size float32, str text.String) op.MacroOp {
func (f *Family) Shape(face text.Face, size float32, str text.String) op.MacroOp {
f.init()
fnt := f.fontFor(face)
ppem := fixed.Int26_6(size * 64)
pk := pathKey{
f: f.font.Font,
f: fnt,
ppem: ppem,
str: str.String,
}
if p, ok := f.faces.pathCache[pk]; ok {
if p, ok := f.pathCache[pk]; ok {
p.active = true
f.faces.pathCache[pk] = p
f.pathCache[pk] = p
return p.path
}
p := textPath(ppem, f.font, str)
f.faces.pathCache[pk] = cachedPath{active: true, path: p}
p := textPath(ppem, &opentype{Font: fnt, Hinting: font.HintingFull}, str)
f.pathCache[pk] = cachedPath{active: true, path: p}
return p
}