mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-04 17:05:38 +00:00
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:
+9
-5
@@ -23,10 +23,13 @@ import (
|
|||||||
|
|
||||||
// Editor implements an editable and scrollable text area.
|
// Editor implements an editable and scrollable text area.
|
||||||
type Editor struct {
|
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
|
Face Face
|
||||||
// Size is the text size. If zero, a reasonable default is used.
|
// Size is the text size. If zero, a default size is used.
|
||||||
Size unit.Value
|
Size unit.Value
|
||||||
|
|
||||||
Alignment Alignment
|
Alignment Alignment
|
||||||
// SingleLine force the text to stay on a single line.
|
// SingleLine force the text to stay on a single line.
|
||||||
// SingleLine also sets the scrolling direction to
|
// SingleLine also sets the scrolling direction to
|
||||||
@@ -241,7 +244,7 @@ func (e *Editor) Layout(gtx *layout.Context) {
|
|||||||
var stack op.StackOp
|
var stack op.StackOp
|
||||||
stack.Push(gtx.Ops)
|
stack.Push(gtx.Ops)
|
||||||
op.TransformOp{}.Offset(lineOff).Add(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)
|
paint.PaintOp{Rect: toRectF(clip).Sub(lineOff)}.Add(gtx.Ops)
|
||||||
stack.Pop()
|
stack.Pop()
|
||||||
}
|
}
|
||||||
@@ -369,7 +372,8 @@ func (e *Editor) layoutText(c unit.Converter) {
|
|||||||
s = e.Hint
|
s = e.Hint
|
||||||
}
|
}
|
||||||
tsize := textSize(c, e.Size)
|
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
|
lines := textLayout.Lines
|
||||||
dims := linesDimens(lines)
|
dims := linesDimens(lines)
|
||||||
for i := 0; i < len(lines)-1; i++ {
|
for i := 0; i < len(lines)-1; i++ {
|
||||||
|
|||||||
+5
-4
@@ -19,10 +19,11 @@ import (
|
|||||||
|
|
||||||
// Label is a widget for laying out and drawing text.
|
// Label is a widget for laying out and drawing text.
|
||||||
type Label struct {
|
type Label struct {
|
||||||
// Face defines the font and style of the text.
|
// Face defines the style of the text.
|
||||||
Face Face
|
Face Face
|
||||||
// Size is the text size. If zero, a default size is used.
|
// Size is the text size. If zero, a default size is used.
|
||||||
Size unit.Value
|
Size unit.Value
|
||||||
|
|
||||||
// Material is a macro recording the material to draw the
|
// Material is a macro recording the material to draw the
|
||||||
// text. Use a ColorOp for colored text.
|
// text. Use a ColorOp for colored text.
|
||||||
Material op.MacroOp
|
Material op.MacroOp
|
||||||
@@ -94,10 +95,10 @@ func (l *lineIterator) Next() (String, f32.Point, bool) {
|
|||||||
return String{}, f32.Point{}, false
|
return String{}, f32.Point{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Label) Layout(gtx *layout.Context) {
|
func (l Label) Layout(gtx *layout.Context, family Family) {
|
||||||
cs := gtx.Constraints
|
cs := gtx.Constraints
|
||||||
tsize := textSize(gtx, l.Size)
|
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
|
lines := textLayout.Lines
|
||||||
if max := l.MaxLines; max > 0 && len(lines) > max {
|
if max := l.MaxLines; max > 0 && len(lines) > max {
|
||||||
lines = lines[:max]
|
lines = lines[:max]
|
||||||
@@ -124,7 +125,7 @@ func (l Label) Layout(gtx *layout.Context) {
|
|||||||
var stack op.StackOp
|
var stack op.StackOp
|
||||||
stack.Push(gtx.Ops)
|
stack.Push(gtx.Ops)
|
||||||
op.TransformOp{}.Offset(off).Add(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.
|
// Set a default color in case the material is empty.
|
||||||
paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(gtx.Ops)
|
paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(gtx.Ops)
|
||||||
l.Material.Add(gtx.Ops)
|
l.Material.Add(gtx.Ops)
|
||||||
|
|||||||
+28
-3
@@ -45,12 +45,27 @@ type LayoutOptions struct {
|
|||||||
SingleLine bool
|
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
|
// Layout returns the text layout for a string given a set of
|
||||||
// options.
|
// 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 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
|
type Alignment uint8
|
||||||
@@ -61,6 +76,16 @@ const (
|
|||||||
Middle
|
Middle
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Regular Style = iota
|
||||||
|
Italic
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Normal Weight = 400
|
||||||
|
Bold Weight = 700
|
||||||
|
)
|
||||||
|
|
||||||
func linesDimens(lines []Line) layout.Dimensions {
|
func linesDimens(lines []Line) layout.Dimensions {
|
||||||
var width fixed.Int26_6
|
var width fixed.Int26_6
|
||||||
var h int
|
var h int
|
||||||
|
|||||||
+39
-42
@@ -19,9 +19,14 @@ import (
|
|||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Faces is a cache of text layouts and paths.
|
// Family is an implementation of text.Family. It caches
|
||||||
type Faces struct {
|
// layouts and paths.
|
||||||
faceCache map[faceKey]*Face
|
// 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
|
layoutCache map[layoutKey]cachedLayout
|
||||||
pathCache map[pathKey]cachedPath
|
pathCache map[pathKey]cachedPath
|
||||||
}
|
}
|
||||||
@@ -49,20 +54,9 @@ type pathKey struct {
|
|||||||
str string
|
str string
|
||||||
}
|
}
|
||||||
|
|
||||||
type faceKey struct {
|
// Reset the cache, discarding any layouts or paths that
|
||||||
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
|
|
||||||
// haven't been used since the last call to Reset.
|
// haven't been used since the last call to Reset.
|
||||||
func (f *Faces) Reset() {
|
func (f *Family) Reset() {
|
||||||
f.init()
|
|
||||||
for pk, p := range f.pathCache {
|
for pk, p := range f.pathCache {
|
||||||
if !p.active {
|
if !p.active {
|
||||||
delete(f.pathCache, pk)
|
delete(f.pathCache, pk)
|
||||||
@@ -81,62 +75,65 @@ func (f *Faces) Reset() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For returns a Face for the given font.
|
// for returns a font for the given face.
|
||||||
func (f *Faces) For(fnt *sfnt.Font) *Face {
|
func (f *Family) fontFor(face text.Face) *sfnt.Font {
|
||||||
f.init()
|
var font *sfnt.Font
|
||||||
fk := faceKey{fnt}
|
switch {
|
||||||
if f, exist := f.faceCache[fk]; exist {
|
case face.Style == text.Italic:
|
||||||
return f
|
font = f.Italic
|
||||||
|
case face.Weight >= 600:
|
||||||
|
font = f.Bold
|
||||||
}
|
}
|
||||||
face := &Face{
|
if font == nil {
|
||||||
faces: f,
|
font = f.Regular
|
||||||
font: &opentype{Font: fnt, Hinting: font.HintingFull},
|
|
||||||
}
|
}
|
||||||
f.faceCache[fk] = face
|
return font
|
||||||
return face
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Faces) init() {
|
func (f *Family) init() {
|
||||||
if f.faceCache != nil {
|
if f.pathCache != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f.faceCache = make(map[faceKey]*Face)
|
|
||||||
f.pathCache = make(map[pathKey]cachedPath)
|
f.pathCache = make(map[pathKey]cachedPath)
|
||||||
f.layoutCache = make(map[layoutKey]cachedLayout)
|
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)
|
ppem := fixed.Int26_6(size * 64)
|
||||||
lk := layoutKey{
|
lk := layoutKey{
|
||||||
f: f.font.Font,
|
f: fnt,
|
||||||
ppem: ppem,
|
ppem: ppem,
|
||||||
str: str,
|
str: str,
|
||||||
opts: opts,
|
opts: opts,
|
||||||
}
|
}
|
||||||
if l, ok := f.faces.layoutCache[lk]; ok {
|
if l, ok := f.layoutCache[lk]; ok {
|
||||||
l.active = true
|
l.active = true
|
||||||
f.faces.layoutCache[lk] = l
|
f.layoutCache[lk] = l
|
||||||
return l.layout
|
return l.layout
|
||||||
}
|
}
|
||||||
l := layoutText(ppem, str, f.font, opts)
|
l := layoutText(ppem, str, &opentype{Font: fnt, Hinting: font.HintingFull}, opts)
|
||||||
f.faces.layoutCache[lk] = cachedLayout{active: true, layout: l}
|
f.layoutCache[lk] = cachedLayout{active: true, layout: l}
|
||||||
return 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)
|
ppem := fixed.Int26_6(size * 64)
|
||||||
pk := pathKey{
|
pk := pathKey{
|
||||||
f: f.font.Font,
|
f: fnt,
|
||||||
ppem: ppem,
|
ppem: ppem,
|
||||||
str: str.String,
|
str: str.String,
|
||||||
}
|
}
|
||||||
if p, ok := f.faces.pathCache[pk]; ok {
|
if p, ok := f.pathCache[pk]; ok {
|
||||||
p.active = true
|
p.active = true
|
||||||
f.faces.pathCache[pk] = p
|
f.pathCache[pk] = p
|
||||||
return p.path
|
return p.path
|
||||||
}
|
}
|
||||||
p := textPath(ppem, f.font, str)
|
p := textPath(ppem, &opentype{Font: fnt, Hinting: font.HintingFull}, str)
|
||||||
f.faces.pathCache[pk] = cachedPath{active: true, path: p}
|
f.pathCache[pk] = cachedPath{active: true, path: p}
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user