mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
font/opentype: [API] support font collection loading
This commit adds back support for loading font collections, which we
lost when switching to the harfbuzz-based shaper last January. In
addition, this commit takes advantage of our new font loading library's
metadata facilities to automatically construct text.FontFaces for all
fonts within a collection. This is significantly more ergonomic for
users, and can be used to load single fonts with automatic metadata
detection as well.
I've exposed a opentype.Face.Font() method that can be used to get the
font metadata for a given face as well, though you have to type assert to
see it:
var myFace text.Face
if asOpentype, ok := myFace.(opentype.Face); ok {
myFont := asOpentype.Font()
}
The one problem with this approach is that the font variant field always
be automatically populated. Mono font detection is supported, but
other variants like SmallCaps are more complicated and may need to be
expressed differently in the future (smallcaps is a feature that any font
file can have, not necessarily a separate font file). See this [0] upstream
issue for details.
Additionally, in order to avoid import cycles, I've moved the declarations
of font attributes to package font. You can fix your code automatically to
refer to the new definitions by running the following:
gofmt -w -r 'text.FontFace -> font.FontFace' .
gofmt -w -r 'text.Variant -> font.Variant' .
gofmt -w -r 'text.Style -> font.Style' .
gofmt -w -r 'text.Typeface -> font.Typeface' .
gofmt -w -r 'text.Font -> font.Font' .
gofmt -w -r 'text.Regular -> font.Regular' .
gofmt -w -r 'text.Italic -> font.Italic' .
gofmt -w -r 'text.Thin -> font.Thin' .
gofmt -w -r 'text.ExtraLight -> font.ExtraLight' .
gofmt -w -r 'text.Light -> font.Light' .
gofmt -w -r 'text.Normal -> font.Normal' .
gofmt -w -r 'text.Medium -> font.Medium' .
gofmt -w -r 'text.SemiBold -> font.SemiBold' .
gofmt -w -r 'text.Bold -> font.Bold' .
gofmt -w -r 'text.ExtraBold -> font.ExtraBold' .
gofmt -w -r 'text.Black -> font.Black' .
gofmt -w -r 'text.Hairline -> font.Thin' .
gofmt -w -r 'text.UltraLight -> font.ExtraLight' .
gofmt -w -r 'text.DemiBold -> font.SemiBold' .
gofmt -w -r 'text.UltraBold -> font.ExtraBold' .
gofmt -w -r 'text.Heavy -> font.Black' .
gofmt -w -r 'text.ExtraBlack -> font.Black+50' .
gofmt -w -r 'text.UltraBlack -> font.ExtraBlack' .
Make sure each affected file imports gioui.org/font.
[0] https://github.com/go-text/typesetting/issues/57
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
This commit is contained in:
+3
-2
@@ -9,6 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/font/gofont"
|
"gioui.org/font/gofont"
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/io/router"
|
"gioui.org/io/router"
|
||||||
@@ -35,7 +36,7 @@ func FuzzIME(f *testing.F) {
|
|||||||
var r router.Router
|
var r router.Router
|
||||||
gtx := layout.Context{Ops: new(op.Ops), Queue: &r}
|
gtx := layout.Context{Ops: new(op.Ops), Queue: &r}
|
||||||
// Layout once to register focus.
|
// Layout once to register focus.
|
||||||
e.Layout(gtx, cache, text.Font{}, unit.Sp(10), op.CallOp{}, op.CallOp{})
|
e.Layout(gtx, cache, font.Font{}, unit.Sp(10), op.CallOp{}, op.CallOp{})
|
||||||
r.Frame(gtx.Ops)
|
r.Frame(gtx.Ops)
|
||||||
|
|
||||||
var state editorState
|
var state editorState
|
||||||
@@ -103,7 +104,7 @@ func FuzzIME(f *testing.F) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmds = cmds[cmdLen:]
|
cmds = cmds[cmdLen:]
|
||||||
e.Layout(gtx, cache, text.Font{}, unit.Sp(10), op.CallOp{}, op.CallOp{})
|
e.Layout(gtx, cache, font.Font{}, unit.Sp(10), op.CallOp{}, op.CallOp{})
|
||||||
r.Frame(gtx.Ops)
|
r.Frame(gtx.Ops)
|
||||||
newState := r.EditorState()
|
newState := r.EditorState()
|
||||||
// We don't track caret position.
|
// We don't track caret position.
|
||||||
|
|||||||
+2
-2
@@ -14,6 +14,7 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/font/opentype"
|
"gioui.org/font/opentype"
|
||||||
"gioui.org/gpu"
|
"gioui.org/gpu"
|
||||||
"gioui.org/internal/ops"
|
"gioui.org/internal/ops"
|
||||||
@@ -25,7 +26,6 @@ import (
|
|||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
"gioui.org/text"
|
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
"gioui.org/widget/material"
|
"gioui.org/widget/material"
|
||||||
@@ -143,7 +143,7 @@ func NewWindow(options ...Option) *Window {
|
|||||||
// Measure decoration height.
|
// Measure decoration height.
|
||||||
deco := new(widget.Decorations)
|
deco := new(widget.Decorations)
|
||||||
face, _ := opentype.Parse(goregular.TTF)
|
face, _ := opentype.Parse(goregular.TTF)
|
||||||
theme := material.NewTheme([]text.FontFace{{Font: text.Font{Typeface: "Go"}, Face: face}})
|
theme := material.NewTheme([]font.FontFace{{Font: font.Font{Typeface: "Go"}, Face: face}})
|
||||||
decoStyle := material.Decorations(theme, deco, 0, "")
|
decoStyle := material.Decorations(theme, deco, 0, "")
|
||||||
gtx := layout.Context{
|
gtx := layout.Context{
|
||||||
Ops: new(op.Ops),
|
Ops: new(op.Ops),
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
Package font provides type describing font faces attributes.
|
||||||
|
*/
|
||||||
|
package font
|
||||||
|
|
||||||
|
import "github.com/go-text/typesetting/font"
|
||||||
|
|
||||||
|
// A FontFace is a Font and a matching Face.
|
||||||
|
type FontFace struct {
|
||||||
|
Font Font
|
||||||
|
Face Face
|
||||||
|
}
|
||||||
|
|
||||||
|
// Style is the font style.
|
||||||
|
type Style int
|
||||||
|
|
||||||
|
// Weight is a font weight, in CSS units subtracted 400 so the zero value
|
||||||
|
// is normal text weight.
|
||||||
|
type Weight int
|
||||||
|
|
||||||
|
// Font specify a particular typeface variant, style and weight.
|
||||||
|
type Font struct {
|
||||||
|
Typeface Typeface
|
||||||
|
Variant Variant
|
||||||
|
Style Style
|
||||||
|
// Weight is the text weight. If zero, Normal is used instead.
|
||||||
|
Weight Weight
|
||||||
|
}
|
||||||
|
|
||||||
|
// Face is an opaque handle to a typeface. The concrete implementation depends
|
||||||
|
// upon the kind of font and shaper in use.
|
||||||
|
type Face interface {
|
||||||
|
Face() font.Face
|
||||||
|
}
|
||||||
|
|
||||||
|
// Typeface identifies a particular typeface design. The empty
|
||||||
|
// string denotes the default typeface.
|
||||||
|
type Typeface string
|
||||||
|
|
||||||
|
// Variant denotes a typeface variant such as "Mono" or "Smallcaps".
|
||||||
|
type Variant string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Regular Style = iota
|
||||||
|
Italic
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Thin Weight = -300
|
||||||
|
ExtraLight Weight = -200
|
||||||
|
Light Weight = -100
|
||||||
|
Normal Weight = 0
|
||||||
|
Medium Weight = 100
|
||||||
|
SemiBold Weight = 200
|
||||||
|
Bold Weight = 300
|
||||||
|
ExtraBold Weight = 400
|
||||||
|
Black Weight = 500
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s Style) String() string {
|
||||||
|
switch s {
|
||||||
|
case Regular:
|
||||||
|
return "Regular"
|
||||||
|
case Italic:
|
||||||
|
return "Italic"
|
||||||
|
default:
|
||||||
|
panic("invalid Style")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w Weight) String() string {
|
||||||
|
switch w {
|
||||||
|
case Thin:
|
||||||
|
return "Thin"
|
||||||
|
case ExtraLight:
|
||||||
|
return "ExtraLight"
|
||||||
|
case Light:
|
||||||
|
return "Light"
|
||||||
|
case Normal:
|
||||||
|
return "Normal"
|
||||||
|
case Medium:
|
||||||
|
return "Medium"
|
||||||
|
case SemiBold:
|
||||||
|
return "SemiBold"
|
||||||
|
case Bold:
|
||||||
|
return "Bold"
|
||||||
|
case ExtraBold:
|
||||||
|
return "ExtraBold"
|
||||||
|
case Black:
|
||||||
|
return "Black"
|
||||||
|
default:
|
||||||
|
panic("invalid Weight")
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-17
@@ -24,29 +24,29 @@ import (
|
|||||||
"golang.org/x/image/font/gofont/gosmallcaps"
|
"golang.org/x/image/font/gofont/gosmallcaps"
|
||||||
"golang.org/x/image/font/gofont/gosmallcapsitalic"
|
"golang.org/x/image/font/gofont/gosmallcapsitalic"
|
||||||
|
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/font/opentype"
|
"gioui.org/font/opentype"
|
||||||
"gioui.org/text"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
once sync.Once
|
once sync.Once
|
||||||
collection []text.FontFace
|
collection []font.FontFace
|
||||||
)
|
)
|
||||||
|
|
||||||
func Collection() []text.FontFace {
|
func Collection() []font.FontFace {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
register(text.Font{}, goregular.TTF)
|
register(font.Font{}, goregular.TTF)
|
||||||
register(text.Font{Style: text.Italic}, goitalic.TTF)
|
register(font.Font{Style: font.Italic}, goitalic.TTF)
|
||||||
register(text.Font{Weight: text.Bold}, gobold.TTF)
|
register(font.Font{Weight: font.Bold}, gobold.TTF)
|
||||||
register(text.Font{Style: text.Italic, Weight: text.Bold}, gobolditalic.TTF)
|
register(font.Font{Style: font.Italic, Weight: font.Bold}, gobolditalic.TTF)
|
||||||
register(text.Font{Weight: text.Medium}, gomedium.TTF)
|
register(font.Font{Weight: font.Medium}, gomedium.TTF)
|
||||||
register(text.Font{Weight: text.Medium, Style: text.Italic}, gomediumitalic.TTF)
|
register(font.Font{Weight: font.Medium, Style: font.Italic}, gomediumitalic.TTF)
|
||||||
register(text.Font{Variant: "Mono"}, gomono.TTF)
|
register(font.Font{Variant: "Mono"}, gomono.TTF)
|
||||||
register(text.Font{Variant: "Mono", Weight: text.Bold}, gomonobold.TTF)
|
register(font.Font{Variant: "Mono", Weight: font.Bold}, gomonobold.TTF)
|
||||||
register(text.Font{Variant: "Mono", Weight: text.Bold, Style: text.Italic}, gomonobolditalic.TTF)
|
register(font.Font{Variant: "Mono", Weight: font.Bold, Style: font.Italic}, gomonobolditalic.TTF)
|
||||||
register(text.Font{Variant: "Mono", Style: text.Italic}, gomonoitalic.TTF)
|
register(font.Font{Variant: "Mono", Style: font.Italic}, gomonoitalic.TTF)
|
||||||
register(text.Font{Variant: "Smallcaps"}, gosmallcaps.TTF)
|
register(font.Font{Variant: "Smallcaps"}, gosmallcaps.TTF)
|
||||||
register(text.Font{Variant: "Smallcaps", Style: text.Italic}, gosmallcapsitalic.TTF)
|
register(font.Font{Variant: "Smallcaps", Style: font.Italic}, gosmallcapsitalic.TTF)
|
||||||
// Ensure that any outside appends will not reuse the backing store.
|
// Ensure that any outside appends will not reuse the backing store.
|
||||||
n := len(collection)
|
n := len(collection)
|
||||||
collection = collection[:n:n]
|
collection = collection[:n:n]
|
||||||
@@ -54,11 +54,11 @@ func Collection() []text.FontFace {
|
|||||||
return collection
|
return collection
|
||||||
}
|
}
|
||||||
|
|
||||||
func register(fnt text.Font, ttf []byte) {
|
func register(fnt font.Font, ttf []byte) {
|
||||||
face, err := opentype.Parse(ttf)
|
face, err := opentype.Parse(ttf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to parse font: %v", err))
|
panic(fmt.Errorf("failed to parse font: %v", err))
|
||||||
}
|
}
|
||||||
fnt.Typeface = "Go"
|
fnt.Typeface = "Go"
|
||||||
collection = append(collection, text.FontFace{Font: fnt, Face: face})
|
collection = append(collection, font.FontFace{Font: fnt, Face: face})
|
||||||
}
|
}
|
||||||
|
|||||||
+113
-3
@@ -15,23 +15,133 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
|
|
||||||
|
giofont "gioui.org/font"
|
||||||
"github.com/go-text/typesetting/font"
|
"github.com/go-text/typesetting/font"
|
||||||
|
fontapi "github.com/go-text/typesetting/opentype/api/font"
|
||||||
|
"github.com/go-text/typesetting/opentype/api/metadata"
|
||||||
|
"github.com/go-text/typesetting/opentype/loader"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Face is a shapeable representation of a font.
|
// Face is a shapeable representation of a font.
|
||||||
type Face struct {
|
type Face struct {
|
||||||
face font.Face
|
face font.Face
|
||||||
|
aspect metadata.Aspect
|
||||||
|
family string
|
||||||
|
variant string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse constructs a Face from source bytes.
|
// Parse constructs a Face from source bytes.
|
||||||
func Parse(src []byte) (Face, error) {
|
func Parse(src []byte) (Face, error) {
|
||||||
face, err := font.ParseTTF(bytes.NewReader(src))
|
ld, err := loader.NewLoader(bytes.NewReader(src))
|
||||||
|
if err != nil {
|
||||||
|
return Face{}, err
|
||||||
|
}
|
||||||
|
face, aspect, family, variant, err := parseLoader(ld)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Face{}, fmt.Errorf("failed parsing truetype font: %w", err)
|
return Face{}, fmt.Errorf("failed parsing truetype font: %w", err)
|
||||||
}
|
}
|
||||||
return Face{face: face}, nil
|
return Face{
|
||||||
|
face: face,
|
||||||
|
aspect: aspect,
|
||||||
|
family: family,
|
||||||
|
variant: variant,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseCollection parse an Opentype font file, with support for collections.
|
||||||
|
// Single font files are supported, returning a slice with length 1.
|
||||||
|
// The returned fonts are automatically wrapped in a text.FontFace with
|
||||||
|
// inferred font metadata.
|
||||||
|
// BUG(whereswaldon): the only Variant that can be detected automatically is
|
||||||
|
// "Mono".
|
||||||
|
func ParseCollection(src []byte) ([]giofont.FontFace, error) {
|
||||||
|
lds, err := loader.NewLoaders(bytes.NewReader(src))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out := make([]giofont.FontFace, len(lds))
|
||||||
|
for i, ld := range lds {
|
||||||
|
face, aspect, family, variant, err := parseLoader(ld)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("reading font %d of collection: %s", i, err)
|
||||||
|
}
|
||||||
|
ff := Face{
|
||||||
|
face: face,
|
||||||
|
aspect: aspect,
|
||||||
|
family: family,
|
||||||
|
variant: variant,
|
||||||
|
}
|
||||||
|
out[i] = giofont.FontFace{
|
||||||
|
Face: ff,
|
||||||
|
Font: ff.Font(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseLoader parses the contents of the loader into a face and its metadata.
|
||||||
|
func parseLoader(ld *loader.Loader) (_ font.Face, _ metadata.Aspect, family, variant string, _ error) {
|
||||||
|
ft, err := fontapi.NewFont(ld)
|
||||||
|
if err != nil {
|
||||||
|
return nil, metadata.Aspect{}, "", "", err
|
||||||
|
}
|
||||||
|
data := metadata.Metadata(ld)
|
||||||
|
if data.IsMonospace {
|
||||||
|
variant = "Mono"
|
||||||
|
}
|
||||||
|
return &fontapi.Face{Font: ft}, data.Aspect, data.Family, variant, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Face) Face() font.Face {
|
func (f Face) Face() font.Face {
|
||||||
return f.face
|
return f.face
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FontFace returns a text.Font with populated font metadata for the
|
||||||
|
// font.
|
||||||
|
// BUG(whereswaldon): the only Variant that can be detected automatically is
|
||||||
|
// "Mono".
|
||||||
|
func (f Face) Font() giofont.Font {
|
||||||
|
return giofont.Font{
|
||||||
|
Typeface: giofont.Typeface(f.family),
|
||||||
|
Style: f.style(),
|
||||||
|
Weight: f.weight(),
|
||||||
|
Variant: giofont.Variant(f.variant),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Face) style() giofont.Style {
|
||||||
|
switch f.aspect.Style {
|
||||||
|
case metadata.StyleItalic:
|
||||||
|
return giofont.Italic
|
||||||
|
case metadata.StyleNormal:
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
return giofont.Regular
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Face) weight() giofont.Weight {
|
||||||
|
switch f.aspect.Weight {
|
||||||
|
case metadata.WeightThin:
|
||||||
|
return giofont.Thin
|
||||||
|
case metadata.WeightExtraLight:
|
||||||
|
return giofont.ExtraLight
|
||||||
|
case metadata.WeightLight:
|
||||||
|
return giofont.Light
|
||||||
|
case metadata.WeightNormal:
|
||||||
|
return giofont.Normal
|
||||||
|
case metadata.WeightMedium:
|
||||||
|
return giofont.Medium
|
||||||
|
case metadata.WeightSemibold:
|
||||||
|
return giofont.SemiBold
|
||||||
|
case metadata.WeightBold:
|
||||||
|
return giofont.Bold
|
||||||
|
case metadata.WeightExtraBold:
|
||||||
|
return giofont.ExtraBold
|
||||||
|
case metadata.WeightBlack:
|
||||||
|
return giofont.Black
|
||||||
|
default:
|
||||||
|
return giofont.Normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ require (
|
|||||||
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d
|
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d
|
||||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2
|
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2
|
||||||
gioui.org/shader v1.0.6
|
gioui.org/shader v1.0.6
|
||||||
github.com/go-text/typesetting v0.0.0-20230405155246-bf9c697c6e16
|
github.com/go-text/typesetting v0.0.0-20230413204129-b4f0492bf7ae
|
||||||
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95
|
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95
|
||||||
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91
|
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91
|
||||||
golang.org/x/image v0.5.0
|
golang.org/x/image v0.5.0
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJG
|
|||||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
||||||
gioui.org/shader v1.0.6 h1:cvZmU+eODFR2545X+/8XucgZdTtEjR3QWW6W65b0q5Y=
|
gioui.org/shader v1.0.6 h1:cvZmU+eODFR2545X+/8XucgZdTtEjR3QWW6W65b0q5Y=
|
||||||
gioui.org/shader v1.0.6/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
|
gioui.org/shader v1.0.6/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
|
||||||
github.com/go-text/typesetting v0.0.0-20230405155246-bf9c697c6e16 h1:DvHeDNqK8cxdZ7C6y88pt3uE7euZH7/LluzyfnUfH/Q=
|
github.com/go-text/typesetting v0.0.0-20230413204129-b4f0492bf7ae h1:LCcaQgYrnS+sx9Tc3oGUvbRBRt+5oFnKWakaxeAvNVI=
|
||||||
github.com/go-text/typesetting v0.0.0-20230405155246-bf9c697c6e16/go.mod h1:zvWM81wAVW6QfVDI6yxfbCuoLnobSYTuMsrXU/u11y8=
|
github.com/go-text/typesetting v0.0.0-20230413204129-b4f0492bf7ae/go.mod h1:KmrpWuSMFcO2yjmyhGpnBGQHSKAoEgMTSSzvLDzCuEA=
|
||||||
github.com/go-text/typesetting-utils v0.0.0-20230326210548-458646692de6 h1:zAAA1U4ykFwqPbcj6YDxvq3F2g0wc/ngPfLJjkR/8zs=
|
github.com/go-text/typesetting-utils v0.0.0-20230412163830-89e4bcfa3ecc h1:9Kf84pnrmmjdRzZIkomfjowmGUhHs20jkrWYw/I6CYc=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
|||||||
+16
-15
@@ -18,6 +18,7 @@ import (
|
|||||||
"golang.org/x/text/unicode/bidi"
|
"golang.org/x/text/unicode/bidi"
|
||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
|
giofont "gioui.org/font"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
"gioui.org/op/clip"
|
"gioui.org/op/clip"
|
||||||
@@ -150,24 +151,24 @@ type runLayout struct {
|
|||||||
|
|
||||||
// faceOrderer chooses the order in which faces should be applied to text.
|
// faceOrderer chooses the order in which faces should be applied to text.
|
||||||
type faceOrderer struct {
|
type faceOrderer struct {
|
||||||
def Font
|
def giofont.Font
|
||||||
faceScratch []font.Face
|
faceScratch []font.Face
|
||||||
fontDefaultOrder map[Font]int
|
fontDefaultOrder map[giofont.Font]int
|
||||||
defaultOrderedFonts []Font
|
defaultOrderedFonts []giofont.Font
|
||||||
faces map[Font]font.Face
|
faces map[giofont.Font]font.Face
|
||||||
faceToIndex map[font.Face]int
|
faceToIndex map[font.Face]int
|
||||||
fonts []Font
|
fonts []giofont.Font
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *faceOrderer) insert(fnt Font, face font.Face) {
|
func (f *faceOrderer) insert(fnt giofont.Font, face font.Face) {
|
||||||
if len(f.fonts) == 0 {
|
if len(f.fonts) == 0 {
|
||||||
f.def = fnt
|
f.def = fnt
|
||||||
}
|
}
|
||||||
if f.fontDefaultOrder == nil {
|
if f.fontDefaultOrder == nil {
|
||||||
f.fontDefaultOrder = make(map[Font]int)
|
f.fontDefaultOrder = make(map[giofont.Font]int)
|
||||||
}
|
}
|
||||||
if f.faces == nil {
|
if f.faces == nil {
|
||||||
f.faces = make(map[Font]font.Face)
|
f.faces = make(map[giofont.Font]font.Face)
|
||||||
f.faceToIndex = make(map[font.Face]int)
|
f.faceToIndex = make(map[font.Face]int)
|
||||||
}
|
}
|
||||||
f.fontDefaultOrder[fnt] = len(f.faceScratch)
|
f.fontDefaultOrder[fnt] = len(f.faceScratch)
|
||||||
@@ -198,7 +199,7 @@ func (c *faceOrderer) faceFor(idx int) font.Face {
|
|||||||
// TODO(whereswaldon): this function could sort all faces by appropriateness for the
|
// TODO(whereswaldon): this function could sort all faces by appropriateness for the
|
||||||
// given font characteristics. This would ensure that (if possible) text using a
|
// given font characteristics. This would ensure that (if possible) text using a
|
||||||
// fallback font would select similar weights and emphases to the primary font.
|
// fallback font would select similar weights and emphases to the primary font.
|
||||||
func (c *faceOrderer) sortedFacesForStyle(font Font) []font.Face {
|
func (c *faceOrderer) sortedFacesForStyle(font giofont.Font) []font.Face {
|
||||||
c.resetFontOrder()
|
c.resetFontOrder()
|
||||||
primary, ok := c.fontForStyle(font)
|
primary, ok := c.fontForStyle(font)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -213,11 +214,11 @@ func (c *faceOrderer) sortedFacesForStyle(font Font) []font.Face {
|
|||||||
|
|
||||||
// fontForStyle returns the closest existing font to the requested font within the
|
// fontForStyle returns the closest existing font to the requested font within the
|
||||||
// same typeface.
|
// same typeface.
|
||||||
func (c *faceOrderer) fontForStyle(font Font) (Font, bool) {
|
func (c *faceOrderer) fontForStyle(font giofont.Font) (giofont.Font, bool) {
|
||||||
if closest, ok := closestFont(font, c.fonts); ok {
|
if closest, ok := closestFont(font, c.fonts); ok {
|
||||||
return closest, true
|
return closest, true
|
||||||
}
|
}
|
||||||
font.Style = Regular
|
font.Style = giofont.Regular
|
||||||
if closest, ok := closestFont(font, c.fonts); ok {
|
if closest, ok := closestFont(font, c.fonts); ok {
|
||||||
return closest, true
|
return closest, true
|
||||||
}
|
}
|
||||||
@@ -226,7 +227,7 @@ func (c *faceOrderer) fontForStyle(font Font) (Font, bool) {
|
|||||||
|
|
||||||
// faces returns a slice of faces with primary as the first element and
|
// faces returns a slice of faces with primary as the first element and
|
||||||
// the remaining faces ordered by insertion order.
|
// the remaining faces ordered by insertion order.
|
||||||
func (f *faceOrderer) sorted(primary Font) []font.Face {
|
func (f *faceOrderer) sorted(primary giofont.Font) []font.Face {
|
||||||
// If we find primary, put it first, and omit it from the below sort.
|
// If we find primary, put it first, and omit it from the below sort.
|
||||||
lowest := 0
|
lowest := 0
|
||||||
for i := range f.fonts {
|
for i := range f.fonts {
|
||||||
@@ -875,9 +876,9 @@ func computeVisualOrder(l *line) {
|
|||||||
|
|
||||||
// closestFont returns the closest Font in available by weight.
|
// closestFont returns the closest Font in available by weight.
|
||||||
// In case of equality the lighter weight will be returned.
|
// In case of equality the lighter weight will be returned.
|
||||||
func closestFont(lookup Font, available []Font) (Font, bool) {
|
func closestFont(lookup giofont.Font, available []giofont.Font) (giofont.Font, bool) {
|
||||||
found := false
|
found := false
|
||||||
var match Font
|
var match giofont.Font
|
||||||
for _, cf := range available {
|
for _, cf := range available {
|
||||||
if cf == lookup {
|
if cf == lookup {
|
||||||
return lookup, true
|
return lookup, true
|
||||||
@@ -902,7 +903,7 @@ func closestFont(lookup Font, available []Font) (Font, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// weightDistance returns the distance value between two font weights.
|
// weightDistance returns the distance value between two font weights.
|
||||||
func weightDistance(wa Weight, wb Weight) int {
|
func weightDistance(wa giofont.Weight, wb giofont.Weight) int {
|
||||||
// Avoid dealing with negative Weight values.
|
// Avoid dealing with negative Weight values.
|
||||||
a := int(wa) + 400
|
a := int(wa) + 400
|
||||||
b := int(wb) + 400
|
b := int(wb) + 400
|
||||||
|
|||||||
+44
-43
@@ -10,6 +10,7 @@ import (
|
|||||||
"golang.org/x/image/font/gofont/goregular"
|
"golang.org/x/image/font/gofont/goregular"
|
||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
|
|
||||||
|
giofont "gioui.org/font"
|
||||||
"gioui.org/font/opentype"
|
"gioui.org/font/opentype"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
)
|
)
|
||||||
@@ -24,7 +25,7 @@ var arabic = system.Locale{
|
|||||||
Direction: system.RTL,
|
Direction: system.RTL,
|
||||||
}
|
}
|
||||||
|
|
||||||
func testShaper(faces ...Face) *shaperImpl {
|
func testShaper(faces ...giofont.Face) *shaperImpl {
|
||||||
shaper := shaperImpl{}
|
shaper := shaperImpl{}
|
||||||
for _, face := range faces {
|
for _, face := range faces {
|
||||||
shaper.Load(FontFace{Face: face})
|
shaper.Load(FontFace{Face: face})
|
||||||
@@ -268,12 +269,12 @@ func makeTestText(shaper *shaperImpl, primaryDir system.TextDirection, fontSize,
|
|||||||
rtlSource = string(complexRunes[:runeLimit])
|
rtlSource = string(complexRunes[:runeLimit])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
simpleText, _ := shaper.shapeAndWrapText(shaper.orderer.sortedFacesForStyle(Font{}), Parameters{
|
simpleText, _ := shaper.shapeAndWrapText(shaper.orderer.sortedFacesForStyle(giofont.Font{}), Parameters{
|
||||||
PxPerEm: fixed.I(fontSize),
|
PxPerEm: fixed.I(fontSize),
|
||||||
MaxWidth: lineWidth,
|
MaxWidth: lineWidth,
|
||||||
Locale: locale,
|
Locale: locale,
|
||||||
}, []rune(simpleSource))
|
}, []rune(simpleSource))
|
||||||
complexText, _ := shaper.shapeAndWrapText(shaper.orderer.sortedFacesForStyle(Font{}), Parameters{
|
complexText, _ := shaper.shapeAndWrapText(shaper.orderer.sortedFacesForStyle(giofont.Font{}), Parameters{
|
||||||
PxPerEm: fixed.I(fontSize),
|
PxPerEm: fixed.I(fontSize),
|
||||||
MaxWidth: lineWidth,
|
MaxWidth: lineWidth,
|
||||||
Locale: locale,
|
Locale: locale,
|
||||||
@@ -642,33 +643,33 @@ func TestTextAppend(t *testing.T) {
|
|||||||
|
|
||||||
func TestClosestFontByWeight(t *testing.T) {
|
func TestClosestFontByWeight(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
testTF1 Typeface = "MockFace"
|
testTF1 giofont.Typeface = "MockFace"
|
||||||
testTF2 Typeface = "TestFace"
|
testTF2 giofont.Typeface = "TestFace"
|
||||||
testTF3 Typeface = "AnotherFace"
|
testTF3 giofont.Typeface = "AnotherFace"
|
||||||
)
|
)
|
||||||
fonts := []Font{
|
fonts := []giofont.Font{
|
||||||
{Typeface: testTF1, Style: Regular, Weight: Normal},
|
{Typeface: testTF1, Style: giofont.Regular, Weight: giofont.Normal},
|
||||||
{Typeface: testTF1, Style: Regular, Weight: Light},
|
{Typeface: testTF1, Style: giofont.Regular, Weight: giofont.Light},
|
||||||
{Typeface: testTF1, Style: Regular, Weight: Bold},
|
{Typeface: testTF1, Style: giofont.Regular, Weight: giofont.Bold},
|
||||||
{Typeface: testTF1, Style: Italic, Weight: Thin},
|
{Typeface: testTF1, Style: giofont.Italic, Weight: giofont.Thin},
|
||||||
}
|
}
|
||||||
weightOnlyTests := []struct {
|
weightOnlyTests := []struct {
|
||||||
Lookup Weight
|
Lookup giofont.Weight
|
||||||
Expected Weight
|
Expected giofont.Weight
|
||||||
}{
|
}{
|
||||||
// Test for existing weights.
|
// Test for existing weights.
|
||||||
{Lookup: Normal, Expected: Normal},
|
{Lookup: giofont.Normal, Expected: giofont.Normal},
|
||||||
{Lookup: Light, Expected: Light},
|
{Lookup: giofont.Light, Expected: giofont.Light},
|
||||||
{Lookup: Bold, Expected: Bold},
|
{Lookup: giofont.Bold, Expected: giofont.Bold},
|
||||||
// Test for missing weights.
|
// Test for missing weights.
|
||||||
{Lookup: Thin, Expected: Light},
|
{Lookup: giofont.Thin, Expected: giofont.Light},
|
||||||
{Lookup: ExtraLight, Expected: Light},
|
{Lookup: giofont.ExtraLight, Expected: giofont.Light},
|
||||||
{Lookup: Medium, Expected: Normal},
|
{Lookup: giofont.Medium, Expected: giofont.Normal},
|
||||||
{Lookup: SemiBold, Expected: Bold},
|
{Lookup: giofont.SemiBold, Expected: giofont.Bold},
|
||||||
{Lookup: ExtraBlack, Expected: Bold},
|
{Lookup: giofont.ExtraBold, Expected: giofont.Bold},
|
||||||
}
|
}
|
||||||
for _, test := range weightOnlyTests {
|
for _, test := range weightOnlyTests {
|
||||||
got, ok := closestFont(Font{Typeface: testTF1, Weight: test.Lookup}, fonts)
|
got, ok := closestFont(giofont.Font{Typeface: testTF1, Weight: test.Lookup}, fonts)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected closest font for %v to exist", test.Lookup)
|
t.Errorf("expected closest font for %v to exist", test.Lookup)
|
||||||
}
|
}
|
||||||
@@ -676,49 +677,49 @@ func TestClosestFontByWeight(t *testing.T) {
|
|||||||
t.Errorf("got weight %v, expected %v", got.Weight, test.Expected)
|
t.Errorf("got weight %v, expected %v", got.Weight, test.Expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fonts = []Font{
|
fonts = []giofont.Font{
|
||||||
{Typeface: testTF1, Style: Regular, Weight: Light},
|
{Typeface: testTF1, Style: giofont.Regular, Weight: giofont.Light},
|
||||||
{Typeface: testTF1, Style: Regular, Weight: Bold},
|
{Typeface: testTF1, Style: giofont.Regular, Weight: giofont.Bold},
|
||||||
{Typeface: testTF1, Style: Italic, Weight: Normal},
|
{Typeface: testTF1, Style: giofont.Italic, Weight: giofont.Normal},
|
||||||
{Typeface: testTF3, Style: Italic, Weight: Bold},
|
{Typeface: testTF3, Style: giofont.Italic, Weight: giofont.Bold},
|
||||||
}
|
}
|
||||||
otherTests := []struct {
|
otherTests := []struct {
|
||||||
Lookup Font
|
Lookup giofont.Font
|
||||||
Expected Font
|
Expected giofont.Font
|
||||||
ExpectedToFail bool
|
ExpectedToFail bool
|
||||||
}{
|
}{
|
||||||
// Test for existing fonts.
|
// Test for existing fonts.
|
||||||
{
|
{
|
||||||
Lookup: Font{Typeface: testTF1, Weight: Light},
|
Lookup: giofont.Font{Typeface: testTF1, Weight: giofont.Light},
|
||||||
Expected: Font{Typeface: testTF1, Style: Regular, Weight: Light},
|
Expected: giofont.Font{Typeface: testTF1, Style: giofont.Regular, Weight: giofont.Light},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Lookup: Font{Typeface: testTF1, Style: Italic, Weight: Normal},
|
Lookup: giofont.Font{Typeface: testTF1, Style: giofont.Italic, Weight: giofont.Normal},
|
||||||
Expected: Font{Typeface: testTF1, Style: Italic, Weight: Normal},
|
Expected: giofont.Font{Typeface: testTF1, Style: giofont.Italic, Weight: giofont.Normal},
|
||||||
},
|
},
|
||||||
// Test for missing fonts.
|
// Test for missing fonts.
|
||||||
{
|
{
|
||||||
Lookup: Font{Typeface: testTF1, Weight: Normal},
|
Lookup: giofont.Font{Typeface: testTF1, Weight: giofont.Normal},
|
||||||
Expected: Font{Typeface: testTF1, Style: Regular, Weight: Light},
|
Expected: giofont.Font{Typeface: testTF1, Style: giofont.Regular, Weight: giofont.Light},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Lookup: Font{Typeface: testTF3, Style: Italic, Weight: Normal},
|
Lookup: giofont.Font{Typeface: testTF3, Style: giofont.Italic, Weight: giofont.Normal},
|
||||||
Expected: Font{Typeface: testTF3, Style: Italic, Weight: Bold},
|
Expected: giofont.Font{Typeface: testTF3, Style: giofont.Italic, Weight: giofont.Bold},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Lookup: Font{Typeface: testTF1, Style: Italic, Weight: Thin},
|
Lookup: giofont.Font{Typeface: testTF1, Style: giofont.Italic, Weight: giofont.Thin},
|
||||||
Expected: Font{Typeface: testTF1, Style: Italic, Weight: Normal},
|
Expected: giofont.Font{Typeface: testTF1, Style: giofont.Italic, Weight: giofont.Normal},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Lookup: Font{Typeface: testTF1, Style: Italic, Weight: Bold},
|
Lookup: giofont.Font{Typeface: testTF1, Style: giofont.Italic, Weight: giofont.Bold},
|
||||||
Expected: Font{Typeface: testTF1, Style: Italic, Weight: Normal},
|
Expected: giofont.Font{Typeface: testTF1, Style: giofont.Italic, Weight: giofont.Normal},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Lookup: Font{Typeface: testTF2, Weight: Normal},
|
Lookup: giofont.Font{Typeface: testTF2, Weight: giofont.Normal},
|
||||||
ExpectedToFail: true,
|
ExpectedToFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Lookup: Font{Typeface: testTF2, Style: Italic, Weight: Normal},
|
Lookup: giofont.Font{Typeface: testTF2, Style: giofont.Italic, Weight: giofont.Normal},
|
||||||
ExpectedToFail: true,
|
ExpectedToFail: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -7,6 +7,7 @@ import (
|
|||||||
"hash/maphash"
|
"hash/maphash"
|
||||||
"image"
|
"image"
|
||||||
|
|
||||||
|
giofont "gioui.org/font"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
"gioui.org/op/clip"
|
"gioui.org/op/clip"
|
||||||
@@ -156,7 +157,7 @@ type layoutKey struct {
|
|||||||
str string
|
str string
|
||||||
truncator string
|
truncator string
|
||||||
locale system.Locale
|
locale system.Locale
|
||||||
font Font
|
font giofont.Font
|
||||||
forceTruncate bool
|
forceTruncate bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-6
@@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
giofont "gioui.org/font"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
"gioui.org/op/clip"
|
"gioui.org/op/clip"
|
||||||
@@ -19,7 +20,7 @@ import (
|
|||||||
// Parameters are static text shaping attributes applied to the entire shaped text.
|
// Parameters are static text shaping attributes applied to the entire shaped text.
|
||||||
type Parameters struct {
|
type Parameters struct {
|
||||||
// Font describes the preferred typeface.
|
// Font describes the preferred typeface.
|
||||||
Font Font
|
Font giofont.Font
|
||||||
// Alignment characterizes the positioning of text within the line. It does not directly
|
// Alignment characterizes the positioning of text within the line. It does not directly
|
||||||
// impact shaping, but is provided in order to allow efficient offset computation.
|
// impact shaping, but is provided in order to allow efficient offset computation.
|
||||||
Alignment Alignment
|
Alignment Alignment
|
||||||
@@ -44,11 +45,7 @@ type Parameters struct {
|
|||||||
forceTruncate bool
|
forceTruncate bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// A FontFace is a Font and a matching Face.
|
type FontFace = giofont.FontFace
|
||||||
type FontFace struct {
|
|
||||||
Font Font
|
|
||||||
Face Face
|
|
||||||
}
|
|
||||||
|
|
||||||
// Glyph describes a shaped font glyph. Many fields are distances relative
|
// Glyph describes a shaped font glyph. Many fields are distances relative
|
||||||
// to the "dot", which is a point on the baseline (the line upon which glyphs
|
// to the "dot", which is a point on the baseline (the line upon which glyphs
|
||||||
|
|||||||
@@ -6,39 +6,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
"github.com/go-text/typesetting/font"
|
|
||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Style is the font style.
|
|
||||||
type Style int
|
|
||||||
|
|
||||||
// Weight is a font weight, in CSS units subtracted 400 so the zero value
|
|
||||||
// is normal text weight.
|
|
||||||
type Weight int
|
|
||||||
|
|
||||||
// Font specify a particular typeface variant, style and weight.
|
|
||||||
type Font struct {
|
|
||||||
Typeface Typeface
|
|
||||||
Variant Variant
|
|
||||||
Style Style
|
|
||||||
// Weight is the text weight. If zero, Normal is used instead.
|
|
||||||
Weight Weight
|
|
||||||
}
|
|
||||||
|
|
||||||
// Face is an opaque handle to a typeface. The concrete implementation depends
|
|
||||||
// upon the kind of font and shaper in use.
|
|
||||||
type Face interface {
|
|
||||||
Face() font.Face
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typeface identifies a particular typeface design. The empty
|
|
||||||
// string denotes the default typeface.
|
|
||||||
type Typeface string
|
|
||||||
|
|
||||||
// Variant denotes a typeface variant such as "Mono" or "Smallcaps".
|
|
||||||
type Variant string
|
|
||||||
|
|
||||||
type Alignment uint8
|
type Alignment uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -47,31 +17,6 @@ const (
|
|||||||
Middle
|
Middle
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
Regular Style = iota
|
|
||||||
Italic
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
Thin Weight = -300
|
|
||||||
ExtraLight Weight = -200
|
|
||||||
Light Weight = -100
|
|
||||||
Normal Weight = 0
|
|
||||||
Medium Weight = 100
|
|
||||||
SemiBold Weight = 200
|
|
||||||
Bold Weight = 300
|
|
||||||
ExtraBold Weight = 400
|
|
||||||
Black Weight = 500
|
|
||||||
|
|
||||||
Hairline = Thin
|
|
||||||
UltraLight = ExtraLight
|
|
||||||
DemiBold = SemiBold
|
|
||||||
UltraBold = ExtraBold
|
|
||||||
Heavy = Black
|
|
||||||
ExtraBlack = Black + 50
|
|
||||||
UltraBlack = ExtraBlack
|
|
||||||
)
|
|
||||||
|
|
||||||
func (a Alignment) String() string {
|
func (a Alignment) String() string {
|
||||||
switch a {
|
switch a {
|
||||||
case Start:
|
case Start:
|
||||||
@@ -109,41 +54,3 @@ func (a Alignment) Align(dir system.TextDirection, width fixed.Int26_6, maxWidth
|
|||||||
panic(fmt.Errorf("unknown alignment %v", a))
|
panic(fmt.Errorf("unknown alignment %v", a))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Style) String() string {
|
|
||||||
switch s {
|
|
||||||
case Regular:
|
|
||||||
return "Regular"
|
|
||||||
case Italic:
|
|
||||||
return "Italic"
|
|
||||||
default:
|
|
||||||
panic("invalid Style")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w Weight) String() string {
|
|
||||||
switch w {
|
|
||||||
case Thin:
|
|
||||||
return "Thin"
|
|
||||||
case ExtraLight:
|
|
||||||
return "ExtraLight"
|
|
||||||
case Light:
|
|
||||||
return "Light"
|
|
||||||
case Normal:
|
|
||||||
return "Normal"
|
|
||||||
case Medium:
|
|
||||||
return "Medium"
|
|
||||||
case SemiBold:
|
|
||||||
return "SemiBold"
|
|
||||||
case Bold:
|
|
||||||
return "Bold"
|
|
||||||
case ExtraBold:
|
|
||||||
return "ExtraBold"
|
|
||||||
case Black:
|
|
||||||
return "Black"
|
|
||||||
case ExtraBlack:
|
|
||||||
return "ExtraBlack"
|
|
||||||
default:
|
|
||||||
panic("invalid Weight")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+2
-1
@@ -13,6 +13,7 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/gesture"
|
"gioui.org/gesture"
|
||||||
"gioui.org/io/clipboard"
|
"gioui.org/io/clipboard"
|
||||||
"gioui.org/io/event"
|
"gioui.org/io/event"
|
||||||
@@ -508,7 +509,7 @@ func (e *Editor) initBuffer() {
|
|||||||
// Layout lays out the editor using the provided textMaterial as the paint material
|
// Layout lays out the editor using the provided textMaterial as the paint material
|
||||||
// for the text glyphs+caret and the selectMaterial as the paint material for the
|
// for the text glyphs+caret and the selectMaterial as the paint material for the
|
||||||
// selection rectangle.
|
// selection rectangle.
|
||||||
func (e *Editor) Layout(gtx layout.Context, lt *text.Shaper, font text.Font, size unit.Sp, textMaterial, selectMaterial op.CallOp) layout.Dimensions {
|
func (e *Editor) Layout(gtx layout.Context, lt *text.Shaper, font font.Font, size unit.Sp, textMaterial, selectMaterial op.CallOp) layout.Dimensions {
|
||||||
e.initBuffer()
|
e.initBuffer()
|
||||||
e.text.Update(gtx, lt, font, size, e.processEvents)
|
e.text.Update(gtx, lt, font, size, e.processEvents)
|
||||||
|
|
||||||
|
|||||||
+20
-19
@@ -17,6 +17,7 @@ import (
|
|||||||
nsareg "eliasnaur.com/font/noto/sans/arabic/regular"
|
nsareg "eliasnaur.com/font/noto/sans/arabic/regular"
|
||||||
"eliasnaur.com/font/roboto/robotoregular"
|
"eliasnaur.com/font/roboto/robotoregular"
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/font/gofont"
|
"gioui.org/font/gofont"
|
||||||
"gioui.org/font/opentype"
|
"gioui.org/font/opentype"
|
||||||
"gioui.org/io/event"
|
"gioui.org/io/event"
|
||||||
@@ -109,7 +110,7 @@ func TestEditorReadOnly(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
e := new(Editor)
|
e := new(Editor)
|
||||||
e.ReadOnly = true
|
e.ReadOnly = true
|
||||||
e.SetText("The quick brown fox jumps over the lazy dog. We just need a few lines of text in the editor so that it can adequately test a few different modes of selection. The quick brown fox jumps over the lazy dog. We just need a few lines of text in the editor so that it can adequately test a few different modes of selection.")
|
e.SetText("The quick brown fox jumps over the lazy dog. We just need a few lines of text in the editor so that it can adequately test a few different modes of selection. The quick brown fox jumps over the lazy dog. We just need a few lines of text in the editor so that it can adequately test a few different modes of selection.")
|
||||||
@@ -188,7 +189,7 @@ func TestEditorConfigurations(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
sentence := "\n\n\n\n\n\n\n\n\n\n\n\nthe quick brown fox jumps over the lazy dog"
|
sentence := "\n\n\n\n\n\n\n\n\n\n\n\nthe quick brown fox jumps over the lazy dog"
|
||||||
runes := len([]rune(sentence))
|
runes := len([]rune(sentence))
|
||||||
|
|
||||||
@@ -242,7 +243,7 @@ func TestEditor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
|
|
||||||
// Regression test for bad in-cluster rune offset math.
|
// Regression test for bad in-cluster rune offset math.
|
||||||
e.SetText("æbc")
|
e.SetText("æbc")
|
||||||
@@ -336,9 +337,9 @@ var arabic = system.Locale{
|
|||||||
Direction: system.RTL,
|
Direction: system.RTL,
|
||||||
}
|
}
|
||||||
|
|
||||||
var arabicCollection = func() []text.FontFace {
|
var arabicCollection = func() []font.FontFace {
|
||||||
parsed, _ := opentype.Parse(nsareg.TTF)
|
parsed, _ := opentype.Parse(nsareg.TTF)
|
||||||
return []text.FontFace{{Font: text.Font{}, Face: parsed}}
|
return []font.FontFace{{Font: font.Font{}, Face: parsed}}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
func TestEditorRTL(t *testing.T) {
|
func TestEditorRTL(t *testing.T) {
|
||||||
@@ -350,7 +351,7 @@ func TestEditorRTL(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(arabicCollection)
|
cache := text.NewShaper(arabicCollection)
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
|
|
||||||
e.SetCaret(0, 0) // shouldn't panic
|
e.SetCaret(0, 0) // shouldn't panic
|
||||||
assertCaret(t, e, 0, 0, 0)
|
assertCaret(t, e, 0, 0, 0)
|
||||||
@@ -417,16 +418,16 @@ func TestEditorLigature(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Skipf("failed parsing test font: %v", err)
|
t.Skipf("failed parsing test font: %v", err)
|
||||||
}
|
}
|
||||||
cache := text.NewShaper([]text.FontFace{
|
cache := text.NewShaper([]font.FontFace{
|
||||||
{
|
{
|
||||||
Font: text.Font{
|
Font: font.Font{
|
||||||
Typeface: "Roboto",
|
Typeface: "Roboto",
|
||||||
},
|
},
|
||||||
Face: face,
|
Face: face,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
In this font, the following rune sequences form ligatures:
|
In this font, the following rune sequences form ligatures:
|
||||||
@@ -540,7 +541,7 @@ func TestEditorDimensions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
dims := e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
dims := e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
||||||
if dims.Size.X == 0 {
|
if dims.Size.X == 0 {
|
||||||
t.Errorf("EditEvent was not reflected in Editor width")
|
t.Errorf("EditEvent was not reflected in Editor width")
|
||||||
@@ -587,7 +588,7 @@ func TestEditorCaretConsistency(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
for _, a := range []text.Alignment{text.Start, text.Middle, text.End} {
|
for _, a := range []text.Alignment{text.Start, text.Middle, text.End} {
|
||||||
e := &Editor{}
|
e := &Editor{}
|
||||||
e.Alignment = a
|
e.Alignment = a
|
||||||
@@ -679,7 +680,7 @@ func TestEditorMoveWord(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
e.SetText(t)
|
e.SetText(t)
|
||||||
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
||||||
return e
|
return e
|
||||||
@@ -784,7 +785,7 @@ func TestEditorInsert(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
e.SetText(t)
|
e.SetText(t)
|
||||||
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
||||||
return e
|
return e
|
||||||
@@ -874,7 +875,7 @@ func TestEditorDeleteWord(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
e.SetText(t)
|
e.SetText(t)
|
||||||
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
||||||
return e
|
return e
|
||||||
@@ -927,7 +928,7 @@ g 2 4 6 8 g
|
|||||||
Locale: english,
|
Locale: english,
|
||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
|
|
||||||
var tim time.Duration
|
var tim time.Duration
|
||||||
@@ -1025,7 +1026,7 @@ func TestSelectMove(t *testing.T) {
|
|||||||
Locale: english,
|
Locale: english,
|
||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
|
|
||||||
// Layout once to populate e.lines and get focus.
|
// Layout once to populate e.lines and get focus.
|
||||||
@@ -1114,7 +1115,7 @@ func TestEditor_MaxLen(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
||||||
|
|
||||||
if got, want := e.Text(), "12345678"; got != want {
|
if got, want := e.Text(), "12345678"; got != want {
|
||||||
@@ -1145,7 +1146,7 @@ func TestEditor_Filter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
||||||
|
|
||||||
if got, want := e.Text(), "12345678"; got != want {
|
if got, want := e.Text(), "12345678"; got != want {
|
||||||
@@ -1169,7 +1170,7 @@ func TestEditor_Submit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
||||||
|
|
||||||
if got, want := e.Text(), "ab1"; got != want {
|
if got, want := e.Text(), "ab1"; got != want {
|
||||||
|
|||||||
+12
-11
@@ -6,6 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
nsareg "eliasnaur.com/font/noto/sans/arabic/regular"
|
nsareg "eliasnaur.com/font/noto/sans/arabic/regular"
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/font/opentype"
|
"gioui.org/font/opentype"
|
||||||
"gioui.org/text"
|
"gioui.org/text"
|
||||||
"golang.org/x/image/font/gofont/goregular"
|
"golang.org/x/image/font/gofont/goregular"
|
||||||
@@ -19,13 +20,13 @@ func makePosTestText(fontSize, lineWidth int, alignOpposite bool) (source string
|
|||||||
ltrFace, _ := opentype.Parse(goregular.TTF)
|
ltrFace, _ := opentype.Parse(goregular.TTF)
|
||||||
rtlFace, _ := opentype.Parse(nsareg.TTF)
|
rtlFace, _ := opentype.Parse(nsareg.TTF)
|
||||||
|
|
||||||
shaper := text.NewShaper([]text.FontFace{
|
shaper := text.NewShaper([]font.FontFace{
|
||||||
{
|
{
|
||||||
Font: text.Font{Typeface: "LTR"},
|
Font: font.Font{Typeface: "LTR"},
|
||||||
Face: ltrFace,
|
Face: ltrFace,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Font: text.Font{Typeface: "RTL"},
|
Font: font.Font{Typeface: "RTL"},
|
||||||
Face: rtlFace,
|
Face: rtlFace,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -33,7 +34,7 @@ func makePosTestText(fontSize, lineWidth int, alignOpposite bool) (source string
|
|||||||
// changing scripts within the RTL).
|
// changing scripts within the RTL).
|
||||||
bidiSource := "The quick سماء שלום لا fox تمط שלום غير the lazy dog."
|
bidiSource := "The quick سماء שלום لا fox تمط שלום غير the lazy dog."
|
||||||
ltrParams := text.Parameters{
|
ltrParams := text.Parameters{
|
||||||
Font: text.Font{Typeface: "LTR"},
|
Font: font.Font{Typeface: "LTR"},
|
||||||
PxPerEm: fixed.I(fontSize),
|
PxPerEm: fixed.I(fontSize),
|
||||||
MaxWidth: lineWidth,
|
MaxWidth: lineWidth,
|
||||||
MinWidth: lineWidth,
|
MinWidth: lineWidth,
|
||||||
@@ -41,7 +42,7 @@ func makePosTestText(fontSize, lineWidth int, alignOpposite bool) (source string
|
|||||||
}
|
}
|
||||||
rtlParams := text.Parameters{
|
rtlParams := text.Parameters{
|
||||||
Alignment: text.End,
|
Alignment: text.End,
|
||||||
Font: text.Font{Typeface: "RTL"},
|
Font: font.Font{Typeface: "RTL"},
|
||||||
PxPerEm: fixed.I(fontSize),
|
PxPerEm: fixed.I(fontSize),
|
||||||
MaxWidth: lineWidth,
|
MaxWidth: lineWidth,
|
||||||
MinWidth: lineWidth,
|
MinWidth: lineWidth,
|
||||||
@@ -68,12 +69,12 @@ func makeAccountingTestText(str string, fontSize, lineWidth int) (txt []text.Gly
|
|||||||
ltrFace, _ := opentype.Parse(goregular.TTF)
|
ltrFace, _ := opentype.Parse(goregular.TTF)
|
||||||
rtlFace, _ := opentype.Parse(nsareg.TTF)
|
rtlFace, _ := opentype.Parse(nsareg.TTF)
|
||||||
|
|
||||||
shaper := text.NewShaper([]text.FontFace{{
|
shaper := text.NewShaper([]font.FontFace{{
|
||||||
Font: text.Font{Typeface: "LTR"},
|
Font: font.Font{Typeface: "LTR"},
|
||||||
Face: ltrFace,
|
Face: ltrFace,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Font: text.Font{Typeface: "RTL"},
|
Font: font.Font{Typeface: "RTL"},
|
||||||
Face: rtlFace,
|
Face: rtlFace,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -94,12 +95,12 @@ func getGlyphs(fontSize, minWidth, lineWidth int, align text.Alignment, str stri
|
|||||||
ltrFace, _ := opentype.Parse(goregular.TTF)
|
ltrFace, _ := opentype.Parse(goregular.TTF)
|
||||||
rtlFace, _ := opentype.Parse(nsareg.TTF)
|
rtlFace, _ := opentype.Parse(nsareg.TTF)
|
||||||
|
|
||||||
shaper := text.NewShaper([]text.FontFace{{
|
shaper := text.NewShaper([]font.FontFace{{
|
||||||
Font: text.Font{Typeface: "LTR"},
|
Font: font.Font{Typeface: "LTR"},
|
||||||
Face: ltrFace,
|
Face: ltrFace,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Font: text.Font{Typeface: "RTL"},
|
Font: font.Font{Typeface: "RTL"},
|
||||||
Face: rtlFace,
|
Face: rtlFace,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
+2
-1
@@ -6,6 +6,7 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/io/semantic"
|
"gioui.org/io/semantic"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
@@ -30,7 +31,7 @@ type Label struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Layout the label with the given shaper, font, size, text, and material.
|
// Layout the label with the given shaper, font, size, text, and material.
|
||||||
func (l Label) Layout(gtx layout.Context, lt *text.Shaper, font text.Font, size unit.Sp, txt string, textMaterial op.CallOp) layout.Dimensions {
|
func (l Label) Layout(gtx layout.Context, lt *text.Shaper, font font.Font, size unit.Sp, txt string, textMaterial op.CallOp) layout.Dimensions {
|
||||||
cs := gtx.Constraints
|
cs := gtx.Constraints
|
||||||
textSize := fixed.I(gtx.Sp(size))
|
textSize := fixed.I(gtx.Sp(size))
|
||||||
lt.LayoutString(text.Parameters{
|
lt.LayoutString(text.Parameters{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"image/color"
|
"image/color"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/internal/f32color"
|
"gioui.org/internal/f32color"
|
||||||
"gioui.org/io/semantic"
|
"gioui.org/io/semantic"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
@@ -22,7 +23,7 @@ type ButtonStyle struct {
|
|||||||
Text string
|
Text string
|
||||||
// Color is the text color.
|
// Color is the text color.
|
||||||
Color color.NRGBA
|
Color color.NRGBA
|
||||||
Font text.Font
|
Font font.Font
|
||||||
TextSize unit.Sp
|
TextSize unit.Sp
|
||||||
Background color.NRGBA
|
Background color.NRGBA
|
||||||
CornerRadius unit.Dp
|
CornerRadius unit.Dp
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/internal/f32color"
|
"gioui.org/internal/f32color"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
@@ -19,7 +20,7 @@ import (
|
|||||||
type checkable struct {
|
type checkable struct {
|
||||||
Label string
|
Label string
|
||||||
Color color.NRGBA
|
Color color.NRGBA
|
||||||
Font text.Font
|
Font font.Font
|
||||||
TextSize unit.Sp
|
TextSize unit.Sp
|
||||||
IconColor color.NRGBA
|
IconColor color.NRGBA
|
||||||
Size unit.Dp
|
Size unit.Dp
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package material
|
|||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/internal/f32color"
|
"gioui.org/internal/f32color"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
@@ -15,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type EditorStyle struct {
|
type EditorStyle struct {
|
||||||
Font text.Font
|
Font font.Font
|
||||||
TextSize unit.Sp
|
TextSize unit.Sp
|
||||||
// Color is the text color.
|
// Color is the text color.
|
||||||
Color color.NRGBA
|
Color color.NRGBA
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package material
|
|||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/internal/f32color"
|
"gioui.org/internal/f32color"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
@@ -19,7 +20,7 @@ import (
|
|||||||
// the label will be non-interactive.
|
// the label will be non-interactive.
|
||||||
type LabelStyle struct {
|
type LabelStyle struct {
|
||||||
// Face defines the text style.
|
// Face defines the text style.
|
||||||
Font text.Font
|
Font font.Font
|
||||||
// Color is the text color.
|
// Color is the text color.
|
||||||
Color color.NRGBA
|
Color color.NRGBA
|
||||||
// SelectionColor is the color of the background for selected text.
|
// SelectionColor is the color of the background for selected text.
|
||||||
@@ -47,13 +48,13 @@ type LabelStyle struct {
|
|||||||
|
|
||||||
func H1(th *Theme, txt string) LabelStyle {
|
func H1(th *Theme, txt string) LabelStyle {
|
||||||
label := Label(th, th.TextSize*96.0/16.0, txt)
|
label := Label(th, th.TextSize*96.0/16.0, txt)
|
||||||
label.Font.Weight = text.Light
|
label.Font.Weight = font.Light
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
|
|
||||||
func H2(th *Theme, txt string) LabelStyle {
|
func H2(th *Theme, txt string) LabelStyle {
|
||||||
label := Label(th, th.TextSize*60.0/16.0, txt)
|
label := Label(th, th.TextSize*60.0/16.0, txt)
|
||||||
label.Font.Weight = text.Light
|
label.Font.Weight = font.Light
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@ func H5(th *Theme, txt string) LabelStyle {
|
|||||||
|
|
||||||
func H6(th *Theme, txt string) LabelStyle {
|
func H6(th *Theme, txt string) LabelStyle {
|
||||||
label := Label(th, th.TextSize*20.0/16.0, txt)
|
label := Label(th, th.TextSize*20.0/16.0, txt)
|
||||||
label.Font.Weight = text.Medium
|
label.Font.Weight = font.Medium
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +82,7 @@ func Subtitle1(th *Theme, txt string) LabelStyle {
|
|||||||
|
|
||||||
func Subtitle2(th *Theme, txt string) LabelStyle {
|
func Subtitle2(th *Theme, txt string) LabelStyle {
|
||||||
label := Label(th, th.TextSize*14.0/16.0, txt)
|
label := Label(th, th.TextSize*14.0/16.0, txt)
|
||||||
label.Font.Weight = text.Medium
|
label.Font.Weight = font.Medium
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/exp/shiny/materialdesign/icons"
|
"golang.org/x/exp/shiny/materialdesign/icons"
|
||||||
|
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/text"
|
"gioui.org/text"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
@@ -46,7 +47,7 @@ type Theme struct {
|
|||||||
FingerSize unit.Dp
|
FingerSize unit.Dp
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTheme(fontCollection []text.FontFace) *Theme {
|
func NewTheme(fontCollection []font.FontFace) *Theme {
|
||||||
t := &Theme{
|
t := &Theme{
|
||||||
Shaper: text.NewShaper(fontCollection),
|
Shaper: text.NewShaper(fontCollection),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/gesture"
|
"gioui.org/gesture"
|
||||||
"gioui.org/io/clipboard"
|
"gioui.org/io/clipboard"
|
||||||
"gioui.org/io/event"
|
"gioui.org/io/event"
|
||||||
@@ -176,7 +177,7 @@ func (l *Selectable) Truncated() bool {
|
|||||||
// Layout clips to the dimensions of the selectable, updates the shaped text, configures input handling, and paints
|
// Layout clips to the dimensions of the selectable, updates the shaped text, configures input handling, and paints
|
||||||
// the text and selection rectangles. The provided textMaterial and selectionMaterial ops are used to set the
|
// the text and selection rectangles. The provided textMaterial and selectionMaterial ops are used to set the
|
||||||
// paint material for the text and selection rectangles, respectively.
|
// paint material for the text and selection rectangles, respectively.
|
||||||
func (l *Selectable) Layout(gtx layout.Context, lt *text.Shaper, font text.Font, size unit.Sp, textMaterial, selectionMaterial op.CallOp) layout.Dimensions {
|
func (l *Selectable) Layout(gtx layout.Context, lt *text.Shaper, font font.Font, size unit.Sp, textMaterial, selectionMaterial op.CallOp) layout.Dimensions {
|
||||||
l.initialize()
|
l.initialize()
|
||||||
l.text.Alignment = l.Alignment
|
l.text.Alignment = l.Alignment
|
||||||
l.text.MaxLines = l.MaxLines
|
l.text.MaxLines = l.MaxLines
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/font/gofont"
|
"gioui.org/font/gofont"
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
@@ -37,7 +38,7 @@ func TestSelectableMove(t *testing.T) {
|
|||||||
Locale: english,
|
Locale: english,
|
||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
font := text.Font{}
|
fnt := font.Font{}
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
|
|
||||||
str := `0123456789`
|
str := `0123456789`
|
||||||
@@ -47,7 +48,7 @@ func TestSelectableMove(t *testing.T) {
|
|||||||
s := new(Selectable)
|
s := new(Selectable)
|
||||||
|
|
||||||
s.SetText(str)
|
s.SetText(str)
|
||||||
s.Layout(gtx, cache, text.Font{}, fontSize, op.CallOp{}, op.CallOp{})
|
s.Layout(gtx, cache, font.Font{}, fontSize, op.CallOp{}, op.CallOp{})
|
||||||
|
|
||||||
testKey := func(keyName string) {
|
testKey := func(keyName string) {
|
||||||
// Select 345
|
// Select 345
|
||||||
@@ -62,7 +63,7 @@ func TestSelectableMove(t *testing.T) {
|
|||||||
// Press the key
|
// Press the key
|
||||||
gtx.Queue = newQueue(key.Event{State: key.Press, Name: keyName})
|
gtx.Queue = newQueue(key.Event{State: key.Press, Name: keyName})
|
||||||
s.SetText(str)
|
s.SetText(str)
|
||||||
s.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
s.Layout(gtx, cache, fnt, fontSize, op.CallOp{}, op.CallOp{})
|
||||||
|
|
||||||
if expected, got := "", s.SelectedText(); expected != got {
|
if expected, got := "", s.SelectedText(); expected != got {
|
||||||
t.Errorf("KeyName %s, expected %q, got %q", keyName, expected, got)
|
t.Errorf("KeyName %s, expected %q, got %q", keyName, expected, got)
|
||||||
@@ -83,7 +84,7 @@ func TestSelectableConfigurations(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(gofont.Collection())
|
cache := text.NewShaper(gofont.Collection())
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
sentence := "\n\n\n\n\n\n\n\n\n\n\n\nthe quick brown fox jumps over the lazy dog"
|
sentence := "\n\n\n\n\n\n\n\n\n\n\n\nthe quick brown fox jumps over the lazy dog"
|
||||||
|
|
||||||
for _, alignment := range []text.Alignment{text.Start, text.Middle, text.End} {
|
for _, alignment := range []text.Alignment{text.Start, text.Middle, text.End} {
|
||||||
|
|||||||
+2
-1
@@ -10,6 +10,7 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
"gioui.org/op/clip"
|
"gioui.org/op/clip"
|
||||||
@@ -222,7 +223,7 @@ func (e *textView) calculateViewSize(gtx layout.Context) image.Point {
|
|||||||
// Update the text, reshaping it as necessary. If not nil, eventHandling will be invoked after reshaping the text to
|
// Update the text, reshaping it as necessary. If not nil, eventHandling will be invoked after reshaping the text to
|
||||||
// allow parent widgets to adapt to any changes in text content or positioning. If eventHandling modifies the contents
|
// allow parent widgets to adapt to any changes in text content or positioning. If eventHandling modifies the contents
|
||||||
// of the textView, it is guaranteed to be reshaped (and ready for painting) before Update returns.
|
// of the textView, it is guaranteed to be reshaped (and ready for painting) before Update returns.
|
||||||
func (e *textView) Update(gtx layout.Context, lt *text.Shaper, font text.Font, size unit.Sp, eventHandling func(gtx layout.Context)) {
|
func (e *textView) Update(gtx layout.Context, lt *text.Shaper, font font.Font, size unit.Sp, eventHandling func(gtx layout.Context)) {
|
||||||
if e.params.Locale != gtx.Locale {
|
if e.params.Locale != gtx.Locale {
|
||||||
e.params.Locale = gtx.Locale
|
e.params.Locale = gtx.Locale
|
||||||
e.invalidate()
|
e.invalidate()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
colEmoji "eliasnaur.com/font/noto/emoji/color"
|
colEmoji "eliasnaur.com/font/noto/emoji/color"
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/font/gofont"
|
"gioui.org/font/gofont"
|
||||||
"gioui.org/font/opentype"
|
"gioui.org/font/opentype"
|
||||||
"gioui.org/gpu/headless"
|
"gioui.org/gpu/headless"
|
||||||
@@ -34,11 +35,11 @@ var (
|
|||||||
}()
|
}()
|
||||||
sizes = []int{10, 100, 1000}
|
sizes = []int{10, 100, 1000}
|
||||||
locales = []system.Locale{arabic, english}
|
locales = []system.Locale{arabic, english}
|
||||||
benchFonts = func() []text.FontFace {
|
benchFonts = func() []font.FontFace {
|
||||||
collection := gofont.Collection()
|
collection := gofont.Collection()
|
||||||
collection = append(collection, arabicCollection...)
|
collection = append(collection, arabicCollection...)
|
||||||
collection = append(collection, text.FontFace{
|
collection = append(collection, font.FontFace{
|
||||||
Font: text.Font{
|
Font: font.Font{
|
||||||
Typeface: "Noto Color Emoji",
|
Typeface: "Noto Color Emoji",
|
||||||
},
|
},
|
||||||
Face: emojiFace,
|
Face: emojiFace,
|
||||||
@@ -91,7 +92,7 @@ func BenchmarkLabelStatic(b *testing.B) {
|
|||||||
defer win.Release()
|
defer win.Release()
|
||||||
}
|
}
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
runes := []rune(txt)[:runeCount]
|
runes := []rune(txt)[:runeCount]
|
||||||
runesStr := string(runes)
|
runesStr := string(runes)
|
||||||
l := Label{}
|
l := Label{}
|
||||||
@@ -123,7 +124,7 @@ func BenchmarkLabelDynamic(b *testing.B) {
|
|||||||
defer win.Release()
|
defer win.Release()
|
||||||
}
|
}
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
runes := []rune(txt)[:runeCount]
|
runes := []rune(txt)[:runeCount]
|
||||||
l := Label{}
|
l := Label{}
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -158,7 +159,7 @@ func BenchmarkEditorStatic(b *testing.B) {
|
|||||||
defer win.Release()
|
defer win.Release()
|
||||||
}
|
}
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
runes := []rune(txt)[:runeCount]
|
runes := []rune(txt)[:runeCount]
|
||||||
runesStr := string(runes)
|
runesStr := string(runes)
|
||||||
e := Editor{}
|
e := Editor{}
|
||||||
@@ -191,7 +192,7 @@ func BenchmarkEditorDynamic(b *testing.B) {
|
|||||||
defer win.Release()
|
defer win.Release()
|
||||||
}
|
}
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
runes := []rune(txt)[:runeCount]
|
runes := []rune(txt)[:runeCount]
|
||||||
e := Editor{}
|
e := Editor{}
|
||||||
e.SetText(string(runes))
|
e.SetText(string(runes))
|
||||||
@@ -225,7 +226,7 @@ func FuzzEditorEditing(f *testing.F) {
|
|||||||
}
|
}
|
||||||
cache := text.NewShaper(benchFonts)
|
cache := text.NewShaper(benchFonts)
|
||||||
fontSize := unit.Sp(10)
|
fontSize := unit.Sp(10)
|
||||||
font := text.Font{}
|
font := font.Font{}
|
||||||
e := Editor{}
|
e := Editor{}
|
||||||
f.Fuzz(func(t *testing.T, txt string, replaceFrom, replaceTo int16) {
|
f.Fuzz(func(t *testing.T, txt string, replaceFrom, replaceTo int16) {
|
||||||
e.SetText(txt)
|
e.SetText(txt)
|
||||||
|
|||||||
Reference in New Issue
Block a user