diff options
| author | Sam Anthony <sam@samanthony.xyz> | 2026-03-03 14:22:43 -0500 |
|---|---|---|
| committer | Sam Anthony <sam@samanthony.xyz> | 2026-03-03 14:22:43 -0500 |
| commit | 0a0b9b8cc9cdc0ffe1819de0266dd1e3c3eb564f (patch) | |
| tree | ead2723b26a2dcb1d1db80efc01390579056d4fc /style/style.go | |
| parent | 8858a54b5ddb3a2d8a42ecb1a837c02800bc934f (diff) | |
| download | gui-0a0b9b8cc9cdc0ffe1819de0266dd1e3c3eb564f.zip | |
style: unit conversion
Implemented the golang.org/x/exp/shiny/unit.Converter interface on
style.Style.
Diffstat (limited to 'style/style.go')
| -rw-r--r-- | style/style.go | 109 |
1 files changed, 106 insertions, 3 deletions
diff --git a/style/style.go b/style/style.go index 5b74952..fe10833 100644 --- a/style/style.go +++ b/style/style.go @@ -1,22 +1,125 @@ package style import ( + "fmt" + "golang.org/x/exp/shiny/unit" + "golang.org/x/image/font" + "golang.org/x/image/font/opentype" "golang.org/x/image/math/fixed" ) -// TODO +const ( + DefaultFontSize = 12.0 // points (1/72") + DefaultDpi = 72.0 // dots per inch + DefaultHinting = font.HintingFull +) + +// Style defines the colors and font faces that widgets are drawn with. type Style struct { + font *opentype.Font + faceOpts opentype.FaceOptions + face font.Face // for measurements + + // TODO +} + +// New returns a new Style with the given options. +// The Style should be closed after use. +func New(opts ...Option) (*Style, error) { + o := parseOpts(opts...) + + fnt, err := opentype.Parse(o.font) + if err != nil { + return nil, err + } + face, err := opentype.NewFace(fnt, &o.FaceOptions) + if err != nil { + return nil, err + } + + return &Style{ + fnt, + o.FaceOptions, + face, + }, nil +} + +func (s *Style) Close() error { + return s.face.Close() +} + +// NewFace returns a new font.Face using the Style's Font and FaceOptions. +func (s *Style) NewFace() (font.Face, error) { + return opentype.NewFace(s.font, &s.faceOpts) } // Convert implements the golang.org/x/exp/shiny/unit.Converter // interface. func (s *Style) Convert(v unit.Value, to unit.Unit) unit.Value { - // TODO + px := fixed26ToFloat(s.Pixels(v)) + var f float64 + switch to { + case unit.Px: + f = px + case unit.Dp: + f = s.pixelsToInches(px) * unit.DensityIndependentPixelsPerInch + case unit.Pt: + f = s.pixelsToInches(px) * unit.PointsPerInch + case unit.Mm: + f = s.pixelsToInches(px) * unit.MillimetresPerInch + case unit.In: + f = s.pixelsToInches(px) + case unit.Em: + f = px / fixed26ToFloat(s.face.Metrics().Height) + case unit.Ex: + f = px / fixed26ToFloat(s.face.Metrics().XHeight) + case unit.Ch: + f = px / fixed26ToFloat(s.zeroWidth()) + default: + panic(fmt.Sprintf("unreachable: impossible %T: %v", to, to)) + } + return unit.Value{f, to} } // Pixels implements the golang.org/x/exp/shiny/unit.Converter // interface. func (s *Style) Pixels(v unit.Value) fixed.Int26_6 { - // TODO + f := floatToFixed26(v.F) + switch v.U { + case unit.Px: + return f + case unit.Dp: + return s.inchesToPixels(v.F / unit.DensityIndependentPixelsPerInch) + case unit.Pt: + return s.inchesToPixels(v.F / unit.PointsPerInch) + case unit.Mm: + return s.inchesToPixels(v.F / unit.MillimetresPerInch) + case unit.In: + return s.inchesToPixels(v.F) + case unit.Em: + return f.Mul(s.face.Metrics().Height) + case unit.Ex: + return f.Mul(s.face.Metrics().XHeight) + case unit.Ch: + return f.Mul(s.zeroWidth()) + default: + panic(fmt.Sprintf("unreachable: impossible %T: %v", v.U, v.U)) + } +} + +func (s *Style) pixelsToInches(px float64) (in float64) { + return px / s.faceOpts.DPI +} + +func (s *Style) inchesToPixels(in float64) (px fixed.Int26_6) { + return floatToFixed26(in * s.faceOpts.DPI) +} + +func (s *Style) zeroWidth() (px fixed.Int26_6) { + if advance, ok := s.face.GlyphAdvance('0'); ok { + return advance + } else { + return floatToFixed26(0.5).Mul(s.face.Metrics().Height) // 0.5em + } } |