all: [API] replace unit.Value with separate unit.Dp, unit.Sp types

The unit.Value is a struct and thus more inconvenient to use than its
underlying float32 type. In addition, most uses don't need a general
value, but rather a specific unit given by the context. This change
replaces unit.Value with two float32 units, Dp and Sp. It also changes
variables and parameters of unit.Value to a specific unit type matching
the context. That is, unit.Dp everywhere except for text sizes which are
in Sp.

Switching to typed float32s has multiple advantages

- They can be constants:

const touchSlop = unit.Dp(16)

- Casting untyped constants is no longer necessary:

insets := layout.UniformInset(16)

- Calculation with values is natural:

func (s ScrollbarStyle) Width() unit.Dp {
	return s.Indicator.MinorWidth + s.Track.MinorPadding + s.Track.MinorPadding
}

The main API change is that calls to gtx.Px must be replaced with either
gtx.Dp or gtx.Sp depending on the unit.

Idea by Christophe Meessen.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2022-04-25 10:08:01 +02:00
parent 48a8540a68
commit 3d37491342
35 changed files with 212 additions and 307 deletions
+2 -2
View File
@@ -35,7 +35,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.Px(10), nil) e.Layout(gtx, cache, text.Font{}, unit.Sp(10), nil)
r.Frame(gtx.Ops) r.Frame(gtx.Ops)
var state editorState var state editorState
@@ -103,7 +103,7 @@ func FuzzIME(f *testing.F) {
} }
} }
cmds = cmds[cmdLen:] cmds = cmds[cmdLen:]
e.Layout(gtx, cache, text.Font{}, unit.Px(10), nil) e.Layout(gtx, cache, text.Font{}, unit.Sp(10), nil)
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.
+10 -8
View File
@@ -154,7 +154,7 @@ type window struct {
dpi int dpi int
fontScale float32 fontScale float32
insets system.Insets insets image.Rectangle
stage system.Stage stage system.Stage
started bool started bool
@@ -586,12 +586,7 @@ func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.
//export Java_org_gioui_GioView_onWindowInsets //export Java_org_gioui_GioView_onWindowInsets
func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C.jlong, top, right, bottom, left C.jint) { func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C.jlong, top, right, bottom, left C.jint) {
w := cgo.Handle(view).Value().(*window) w := cgo.Handle(view).Value().(*window)
w.insets = system.Insets{ w.insets = image.Rect(int(left), int(top), int(right), int(bottom))
Top: unit.Px(float32(top)),
Bottom: unit.Px(float32(bottom)),
Left: unit.Px(float32(left)),
Right: unit.Px(float32(right)),
}
if w.stage >= system.StageRunning { if w.stage >= system.StageRunning {
w.draw(env, true) w.draw(env, true)
} }
@@ -830,11 +825,18 @@ func (w *window) draw(env *C.JNIEnv, sync bool) {
} }
const inchPrDp = 1.0 / 160 const inchPrDp = 1.0 / 160
ppdp := float32(w.dpi) * inchPrDp ppdp := float32(w.dpi) * inchPrDp
dppp := unit.Dp(1.0 / ppdp)
insets := system.Insets{
Top: unit.Dp(w.insets.Min.Y) * dppp,
Bottom: unit.Dp(w.insets.Max.Y) * dppp,
Left: unit.Dp(w.insets.Min.X) * dppp,
Right: unit.Dp(w.insets.Max.X) * dppp,
}
w.callbacks.Event(frameEvent{ w.callbacks.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: w.config.Size, Size: w.config.Size,
Insets: w.insets, Insets: insets,
Metric: unit.Metric{ Metric: unit.Metric{
PxPerDp: ppdp, PxPerDp: ppdp,
PxPerSp: w.fontScale * ppdp, PxPerSp: w.fontScale * ppdp,
+10 -8
View File
@@ -150,6 +150,11 @@ func (w *window) draw(sync bool) {
w.w.Event(system.StageEvent{Stage: system.StageRunning}) w.w.Event(system.StageEvent{Stage: system.StageRunning})
} }
const inchPrDp = 1.0 / 163 const inchPrDp = 1.0 / 163
m := unit.Metric{
PxPerDp: float32(params.dpi) * inchPrDp,
PxPerSp: float32(params.sdpi) * inchPrDp,
}
dppp := unit.Dp(1. / m.PxPerDp)
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
@@ -158,15 +163,12 @@ func (w *window) draw(sync bool) {
Y: int(params.height + .5), Y: int(params.height + .5),
}, },
Insets: system.Insets{ Insets: system.Insets{
Top: unit.Px(float32(params.top)), Top: unit.Dp(params.top) * dppp,
Bottom: unit.Px(float32(params.bottom)), Bottom: unit.Dp(params.bottom) * dppp,
Left: unit.Px(float32(params.left)), Left: unit.Dp(params.left) * dppp,
Right: unit.Px(float32(params.right)), Right: unit.Dp(params.right) * dppp,
},
Metric: unit.Metric{
PxPerDp: float32(params.dpi) * inchPrDp,
PxPerSp: float32(params.sdpi) * inchPrDp,
}, },
Metric: m,
}, },
Sync: sync, Sync: sync,
}) })
+3 -2
View File
@@ -643,10 +643,11 @@ func (w *window) draw(sync bool) {
} }
func (w *window) getConfig() (image.Point, system.Insets, unit.Metric) { func (w *window) getConfig() (image.Point, system.Insets, unit.Metric) {
invscale := unit.Dp(1. / w.scale)
return image.Pt(w.config.Size.X, w.config.Size.Y), return image.Pt(w.config.Size.X, w.config.Size.Y),
system.Insets{ system.Insets{
Bottom: unit.Px(w.inset.Y), Bottom: unit.Dp(w.inset.Y) * invscale,
Right: unit.Px(w.inset.X), Right: unit.Dp(w.inset.X) * invscale,
}, unit.Metric{ }, unit.Metric{
PxPerDp: w.scale, PxPerDp: w.scale,
PxPerSp: w.scale, PxPerSp: w.scale,
+22 -22
View File
@@ -138,7 +138,7 @@ var ackEvent event.Event
// iOS, Android, WebAssembly. // iOS, Android, WebAssembly.
func NewWindow(options ...Option) *Window { func NewWindow(options ...Option) *Window {
defaultOptions := []Option{ defaultOptions := []Option{
Size(unit.Dp(800), unit.Dp(600)), Size(800, 600),
Title("Gio"), Title("Gio"),
} }
options = append(defaultOptions, options...) options = append(defaultOptions, options...)
@@ -546,8 +546,8 @@ func (w *Window) moveFocus(dir router.FocusDirection, d driver) {
default: default:
return return
} }
const scrollABit = 50 const scrollABit = unit.Dp(50)
dist := v.Mul(w.metric.Px(unit.Dp(scrollABit))) dist := v.Mul(int(w.metric.Dp(scrollABit)))
w.queue.q.ScrollFocus(dist) w.queue.q.ScrollFocus(dist)
} }
w.setNextFrame(time.Time{}) w.setNextFrame(time.Time{})
@@ -805,12 +805,12 @@ func (w *Window) processEvent(d driver, e event.Event) bool {
wrapper.Reset() wrapper.Reset()
viewport := image.Rectangle{ viewport := image.Rectangle{
Min: image.Point{ Min: image.Point{
X: e2.Metric.Px(e2.Insets.Left), X: e2.Metric.Dp(e2.Insets.Left),
Y: e2.Metric.Px(e2.Insets.Top), Y: e2.Metric.Dp(e2.Insets.Top),
}, },
Max: image.Point{ Max: image.Point{
X: e2.Size.X - e2.Metric.Px(e2.Insets.Right), X: e2.Size.X - e2.Metric.Dp(e2.Insets.Right),
Y: e2.Size.Y - e2.Metric.Px(e2.Insets.Bottom), Y: e2.Size.Y - e2.Metric.Dp(e2.Insets.Bottom),
}, },
} }
// Scroll to focus if viewport is shrinking in any dimension. // Scroll to focus if viewport is shrinking in any dimension.
@@ -1034,50 +1034,50 @@ func Title(t string) Option {
} }
// Size sets the size of the window. The mode will be changed to Windowed. // Size sets the size of the window. The mode will be changed to Windowed.
func Size(w, h unit.Value) Option { func Size(w, h unit.Dp) Option {
if w.V <= 0 { if w <= 0 {
panic("width must be larger than or equal to 0") panic("width must be larger than or equal to 0")
} }
if h.V <= 0 { if h <= 0 {
panic("height must be larger than or equal to 0") panic("height must be larger than or equal to 0")
} }
return func(m unit.Metric, cnf *Config) { return func(m unit.Metric, cnf *Config) {
cnf.Mode = Windowed cnf.Mode = Windowed
cnf.Size = image.Point{ cnf.Size = image.Point{
X: m.Px(w), X: m.Dp(w),
Y: m.Px(h), Y: m.Dp(h),
} }
} }
} }
// MaxSize sets the maximum size of the window. // MaxSize sets the maximum size of the window.
func MaxSize(w, h unit.Value) Option { func MaxSize(w, h unit.Dp) Option {
if w.V <= 0 { if w <= 0 {
panic("width must be larger than or equal to 0") panic("width must be larger than or equal to 0")
} }
if h.V <= 0 { if h <= 0 {
panic("height must be larger than or equal to 0") panic("height must be larger than or equal to 0")
} }
return func(m unit.Metric, cnf *Config) { return func(m unit.Metric, cnf *Config) {
cnf.MaxSize = image.Point{ cnf.MaxSize = image.Point{
X: m.Px(w), X: m.Dp(w),
Y: m.Px(h), Y: m.Dp(h),
} }
} }
} }
// MinSize sets the minimum size of the window. // MinSize sets the minimum size of the window.
func MinSize(w, h unit.Value) Option { func MinSize(w, h unit.Dp) Option {
if w.V <= 0 { if w <= 0 {
panic("width must be larger than or equal to 0") panic("width must be larger than or equal to 0")
} }
if h.V <= 0 { if h <= 0 {
panic("height must be larger than or equal to 0") panic("height must be larger than or equal to 0")
} }
return func(m unit.Metric, cnf *Config) { return func(m unit.Metric, cnf *Config) {
cnf.MinSize = image.Point{ cnf.MinSize = image.Point{
X: m.Px(w), X: m.Dp(w),
Y: m.Px(h), Y: m.Dp(h),
} }
} }
} }
+4 -4
View File
@@ -157,7 +157,7 @@ const (
StateFlinging StateFlinging
) )
var touchSlop = unit.Dp(3) const touchSlop = unit.Dp(3)
// Add the handler to the operation list to receive click events. // Add the handler to the operation list to receive click events.
func (c *Click) Add(ops *op.Ops) { func (c *Click) Add(ops *op.Ops) {
@@ -303,7 +303,7 @@ func (s *Scroll) Scroll(cfg unit.Metric, q event.Queue, t time.Time, axis Axis)
break break
} }
fling := s.estimator.Estimate() fling := s.estimator.Estimate()
if slop, d := float32(cfg.Px(touchSlop)), fling.Distance; d < -slop || d > slop { if slop, d := float32(cfg.Dp(touchSlop)), fling.Distance; d < -slop || d > slop {
s.flinger.Start(cfg, t, fling.Velocity) s.flinger.Start(cfg, t, fling.Velocity)
} }
fallthrough fallthrough
@@ -329,7 +329,7 @@ func (s *Scroll) Scroll(cfg unit.Metric, q event.Queue, t time.Time, axis Axis)
v := int(math.Round(float64(val))) v := int(math.Round(float64(val)))
dist := s.last - v dist := s.last - v
if e.Priority < pointer.Grabbed { if e.Priority < pointer.Grabbed {
slop := cfg.Px(touchSlop) slop := cfg.Dp(touchSlop)
if dist := dist; dist >= slop || -slop >= dist { if dist := dist; dist >= slop || -slop >= dist {
s.grab = true s.grab = true
} }
@@ -407,7 +407,7 @@ func (d *Drag) Events(cfg unit.Metric, q event.Queue, axis Axis) []pointer.Event
} }
if e.Priority < pointer.Grabbed { if e.Priority < pointer.Grabbed {
diff := e.Position.Sub(d.start) diff := e.Position.Sub(d.start)
slop := cfg.Px(touchSlop) slop := cfg.Dp(touchSlop)
if diff.X*diff.X+diff.Y*diff.Y > float32(slop*slop) { if diff.X*diff.X+diff.Y*diff.Y > float32(slop*slop) {
d.grab = true d.grab = true
} }
+5 -8
View File
@@ -19,25 +19,22 @@ type Animation struct {
v0 float32 v0 float32
} }
var (
// Pixels/second.
minFlingVelocity = unit.Dp(50)
maxFlingVelocity = unit.Dp(8000)
)
const ( const (
// dp/second.
minFlingVelocity = unit.Dp(50)
maxFlingVelocity = unit.Dp(8000)
thresholdVelocity = 1 thresholdVelocity = 1
) )
// Start a fling given a starting velocity. Returns whether a // Start a fling given a starting velocity. Returns whether a
// fling was started. // fling was started.
func (f *Animation) Start(c unit.Metric, now time.Time, velocity float32) bool { func (f *Animation) Start(c unit.Metric, now time.Time, velocity float32) bool {
min := float32(c.Px(minFlingVelocity)) min := float32(c.Dp(minFlingVelocity))
v := velocity v := velocity
if -min <= v && v <= min { if -min <= v && v <= min {
return false return false
} }
max := float32(c.Px(maxFlingVelocity)) max := float32(c.Dp(maxFlingVelocity))
if v > max { if v > max {
v = max v = max
} else if v < -max { } else if v < -max {
+2 -1
View File
@@ -45,7 +45,8 @@ type DestroyEvent struct {
// system decoration such as translucent // system decoration such as translucent
// system bars and software keyboards. // system bars and software keyboards.
type Insets struct { type Insets struct {
Top, Bottom, Left, Right unit.Value // Values are in pixels.
Top, Bottom, Left, Right unit.Dp
} }
// A StageEvent is generated whenever the stage of a // A StageEvent is generated whenever the stage of a
+12 -7
View File
@@ -52,15 +52,15 @@ func NewContext(ops *op.Ops, e system.FrameEvent) Context {
size := e.Size size := e.Size
if e.Insets != (system.Insets{}) { if e.Insets != (system.Insets{}) {
left := e.Metric.Px(e.Insets.Left) left := e.Metric.Dp(e.Insets.Left)
top := e.Metric.Px(e.Insets.Top) top := e.Metric.Dp(e.Insets.Top)
op.Offset(image.Point{ op.Offset(image.Point{
X: left, X: left,
Y: top, Y: top,
}).Add(ops) }).Add(ops)
size.X -= left + e.Metric.Px(e.Insets.Right) size.X -= left + e.Metric.Dp(e.Insets.Right)
size.Y -= top + e.Metric.Px(e.Insets.Bottom) size.Y -= top + e.Metric.Dp(e.Insets.Bottom)
} }
return Context{ return Context{
@@ -72,9 +72,14 @@ func NewContext(ops *op.Ops, e system.FrameEvent) Context {
} }
} }
// Px maps the value to pixels. // Dp converts v to pixels.
func (c Context) Px(v unit.Value) int { func (c Context) Dp(v unit.Dp) int {
return c.Metric.Px(v) return c.Metric.Dp(v)
}
// Sp converts v to pixels.
func (c Context) Sp(v unit.Sp) int {
return c.Metric.Sp(v)
} }
// Events returns the events available for the key. If no // Events returns the events available for the key. If no
+1 -1
View File
@@ -16,7 +16,7 @@ For example, to add space above a widget:
var gtx layout.Context var gtx layout.Context
// Configure a top inset. // Configure a top inset.
inset := layout.Inset{Top: unit.Dp(8), ...} inset := layout.Inset{Top: 8, ...}
// Use the inset to lay out a widget. // Use the inset to lay out a widget.
inset.Layout(gtx, func() { inset.Layout(gtx, func() {
// Lay out widget and determine its size given the constraints // Lay out widget and determine its size given the constraints
+1 -2
View File
@@ -6,7 +6,6 @@ import (
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
"gioui.org/unit"
) )
func ExampleInset() { func ExampleInset() {
@@ -19,7 +18,7 @@ func ExampleInset() {
} }
// Inset all edges by 10. // Inset all edges by 10.
inset := layout.UniformInset(unit.Dp(10)) inset := layout.UniformInset(10)
dims := inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { dims := inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
// Lay out a 50x50 sized widget. // Lay out a 50x50 sized widget.
dims := layoutWidget(gtx, 50, 50) dims := layoutWidget(gtx, 50, 50)
+9 -9
View File
@@ -113,15 +113,15 @@ func (c Constraints) Constrain(size image.Point) image.Point {
// constraints. The minimum constraints will be adjusted to ensure // constraints. The minimum constraints will be adjusted to ensure
// they do not exceed the maximum. // they do not exceed the maximum.
type Inset struct { type Inset struct {
Top, Bottom, Left, Right unit.Value Top, Bottom, Left, Right unit.Dp
} }
// Layout a widget. // Layout a widget.
func (in Inset) Layout(gtx Context, w Widget) Dimensions { func (in Inset) Layout(gtx Context, w Widget) Dimensions {
top := gtx.Px(in.Top) top := gtx.Dp(in.Top)
right := gtx.Px(in.Right) right := gtx.Dp(in.Right)
bottom := gtx.Px(in.Bottom) bottom := gtx.Dp(in.Bottom)
left := gtx.Px(in.Left) left := gtx.Dp(in.Left)
mcs := gtx.Constraints mcs := gtx.Constraints
mcs.Max.X -= left + right mcs.Max.X -= left + right
if mcs.Max.X < 0 { if mcs.Max.X < 0 {
@@ -153,7 +153,7 @@ func (in Inset) Layout(gtx Context, w Widget) Dimensions {
// UniformInset returns an Inset with a single inset applied to all // UniformInset returns an Inset with a single inset applied to all
// edges. // edges.
func UniformInset(v unit.Value) Inset { func UniformInset(v unit.Dp) Inset {
return Inset{Top: v, Right: v, Bottom: v, Left: v} return Inset{Top: v, Right: v, Bottom: v, Left: v}
} }
@@ -213,14 +213,14 @@ func (d Direction) Position(widget, bounds image.Point) image.Point {
// Spacer adds space between widgets. // Spacer adds space between widgets.
type Spacer struct { type Spacer struct {
Width, Height unit.Value Width, Height unit.Dp
} }
func (s Spacer) Layout(gtx Context) Dimensions { func (s Spacer) Layout(gtx Context) Dimensions {
return Dimensions{ return Dimensions{
Size: image.Point{ Size: image.Point{
X: gtx.Px(s.Width), X: gtx.Dp(s.Width),
Y: gtx.Px(s.Height), Y: gtx.Dp(s.Height),
}, },
} }
} }
+18 -114
View File
@@ -2,9 +2,7 @@
/* /*
Package unit implements device independent units and values. Package unit implements device independent units.
A Value is a value with a Unit attached.
Device independent pixel, or dp, is the unit for sizes independent of Device independent pixel, or dp, is the unit for sizes independent of
the underlying display device. the underlying display device.
@@ -23,19 +21,9 @@ values.
package unit package unit
import ( import (
"fmt"
"math" "math"
) )
// Value is a value with a unit.
type Value struct {
V float32
U Unit
}
// Unit represents a unit for a Value.
type Unit uint8
// Metric converts Values to device-dependent pixels, px. The zero // Metric converts Values to device-dependent pixels, px. The zero
// value represents a 1-to-1 scale from dp, sp to pixels. // value represents a 1-to-1 scale from dp, sp to pixels.
type Metric struct { type Metric struct {
@@ -45,113 +33,29 @@ type Metric struct {
PxPerSp float32 PxPerSp float32
} }
const ( type (
// UnitPx represent device pixels in the resolution of // Dp represents device independent pixels. 1 dp will
// the underlying display.
UnitPx Unit = iota
// UnitDp represents device independent pixels. 1 dp will
// have the same apparent size across platforms and // have the same apparent size across platforms and
// display resolutions. // display resolutions.
UnitDp Dp float32
// UnitSp is like UnitDp but for font sizes. // Sp is like UnitDp but for font sizes.
UnitSp Sp float32
) )
// Px returns the Value for v device pixels. // Dp converts v to pixels, rounded to the nearest integer value.
func Px(v float32) Value { func (c Metric) Dp(v Dp) int {
return Value{V: v, U: UnitPx} s := c.PxPerDp
} if s == 0. {
s = 1.
// Dp returns the Value for v device independent
// pixels.
func Dp(v float32) Value {
return Value{V: v, U: UnitDp}
}
// Sp returns the Value for v scaled dps.
func Sp(v float32) Value {
return Value{V: v, U: UnitSp}
}
// Scale returns the value scaled by s.
func (v Value) Scale(s float32) Value {
v.V *= s
return v
}
func (v Value) String() string {
return fmt.Sprintf("%g%s", v.V, v.U)
}
func (u Unit) String() string {
switch u {
case UnitPx:
return "px"
case UnitDp:
return "dp"
case UnitSp:
return "sp"
default:
panic("unknown unit")
} }
return int(math.Round(float64(s) * float64(v)))
} }
// Add a list of Values. // Sp converts v to pixels, rounded to the nearest integer value.
func Add(c Metric, values ...Value) Value { func (c Metric) Sp(v Sp) int {
var sum Value s := c.PxPerSp
for _, v := range values { if s == 0. {
sum, v = compatible(c, sum, v) s = 1.
sum.V += v.V
} }
return sum return int(math.Round(float64(s) * float64(v)))
}
// Max returns the maximum of a list of Values.
func Max(c Metric, values ...Value) Value {
var max Value
for _, v := range values {
max, v = compatible(c, max, v)
if v.V > max.V {
max.V = v.V
}
}
return max
}
func (c Metric) Px(v Value) int {
var r float32
switch v.U {
case UnitPx:
r = v.V
case UnitDp:
s := c.PxPerDp
if s == 0 {
s = 1
}
r = s * v.V
case UnitSp:
s := c.PxPerSp
if s == 0 {
s = 1
}
r = s * v.V
default:
panic("unknown unit")
}
return int(math.Round(float64(r)))
}
func compatible(c Metric, v1, v2 Value) (Value, Value) {
if v1.U == v2.U {
return v1, v2
}
if v1.V == 0 {
v1.U = v2.U
return v1, v2
}
if v2.V == 0 {
v2.U = v1.U
return v1, v2
}
return Px(float32(c.Px(v1))), Px(float32(c.Px(v2)))
} }
+4 -4
View File
@@ -15,16 +15,16 @@ import (
// Border lays out a widget and draws a border inside it. // Border lays out a widget and draws a border inside it.
type Border struct { type Border struct {
Color color.NRGBA Color color.NRGBA
CornerRadius unit.Value CornerRadius unit.Dp
Width unit.Value Width unit.Dp
} }
func (b Border) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions { func (b Border) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
dims := w(gtx) dims := w(gtx)
sz := dims.Size sz := dims.Size
rr := gtx.Px(b.CornerRadius) rr := gtx.Dp(b.CornerRadius)
width := gtx.Px(b.Width) width := gtx.Dp(b.Width)
sz.X -= width sz.X -= width
sz.Y -= width sz.Y -= width
+1 -2
View File
@@ -10,7 +10,6 @@ import (
"gioui.org/io/system" "gioui.org/io/system"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op/clip" "gioui.org/op/clip"
"gioui.org/unit"
) )
// Decorations handles the states of window decorations. // Decorations handles the states of window decorations.
@@ -67,7 +66,7 @@ func (d *Decorations) Clickable(action system.Action) *Clickable {
// LayoutResize lays out the resize actions. // LayoutResize lays out the resize actions.
func (d *Decorations) LayoutResize(gtx layout.Context, actions system.Action) { func (d *Decorations) LayoutResize(gtx layout.Context, actions system.Action) {
cs := gtx.Constraints.Max cs := gtx.Constraints.Max
wh := gtx.Px(unit.Dp(10)) wh := gtx.Dp(10)
s := []struct { s := []struct {
system.Action system.Action
image.Rectangle image.Rectangle
+3 -3
View File
@@ -497,12 +497,12 @@ func (e *Editor) calculateViewSize(gtx layout.Context) image.Point {
} }
// Layout lays out the editor. If content is not nil, it is laid out on top. // Layout lays out the editor. If content is not nil, it is laid out on top.
func (e *Editor) Layout(gtx layout.Context, sh text.Shaper, font text.Font, size unit.Value, content layout.Widget) layout.Dimensions { func (e *Editor) Layout(gtx layout.Context, sh text.Shaper, font text.Font, size unit.Sp, content layout.Widget) layout.Dimensions {
if e.locale != gtx.Locale { if e.locale != gtx.Locale {
e.locale = gtx.Locale e.locale = gtx.Locale
e.invalidate() e.invalidate()
} }
textSize := fixed.I(gtx.Px(size)) textSize := fixed.I(gtx.Sp(size))
if e.font != font || e.textSize != textSize { if e.font != font || e.textSize != textSize {
e.invalidate() e.invalidate()
e.font = font e.font = font
@@ -776,7 +776,7 @@ func (e *Editor) PaintText(gtx layout.Context) {
// caretWidth returns the width occupied by the caret for the current // caretWidth returns the width occupied by the caret for the current
// gtx. // gtx.
func (e *Editor) caretWidth(gtx layout.Context) int { func (e *Editor) caretWidth(gtx layout.Context) int {
carWidth2 := gtx.Px(unit.Dp(1)) / 2 carWidth2 := gtx.Dp(1) / 2
if carWidth2 < 1 { if carWidth2 < 1 {
carWidth2 = 1 carWidth2 = 1
} }
+12 -12
View File
@@ -47,7 +47,7 @@ func TestEditorZeroDimensions(t *testing.T) {
Locale: english, Locale: english,
} }
cache := text.NewCache(gofont.Collection()) cache := text.NewCache(gofont.Collection())
fontSize := unit.Px(10) fontSize := unit.Sp(10)
font := text.Font{} font := text.Font{}
e := new(Editor) e := new(Editor)
dims := e.Layout(gtx, cache, font, fontSize, nil) dims := e.Layout(gtx, cache, font, fontSize, nil)
@@ -63,7 +63,7 @@ func TestEditorConfigurations(t *testing.T) {
Locale: english, Locale: english,
} }
cache := text.NewCache(gofont.Collection()) cache := text.NewCache(gofont.Collection())
fontSize := unit.Px(10) fontSize := unit.Sp(10)
font := text.Font{} font := text.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))
@@ -103,7 +103,7 @@ func TestEditor(t *testing.T) {
Locale: english, Locale: english,
} }
cache := text.NewCache(gofont.Collection()) cache := text.NewCache(gofont.Collection())
fontSize := unit.Px(10) fontSize := unit.Sp(10)
font := text.Font{} font := text.Font{}
// Regression test for bad in-cluster rune offset math. // Regression test for bad in-cluster rune offset math.
@@ -206,7 +206,7 @@ func TestEditorRTL(t *testing.T) {
Locale: arabic, Locale: arabic,
} }
cache := text.NewCache(arabicCollection) cache := text.NewCache(arabicCollection)
fontSize := unit.Px(10) fontSize := unit.Sp(10)
font := text.Font{} font := text.Font{}
e.SetCaret(0, 0) // shouldn't panic e.SetCaret(0, 0) // shouldn't panic
@@ -282,7 +282,7 @@ func TestEditorLigature(t *testing.T) {
Face: face, Face: face,
}, },
}) })
fontSize := unit.Px(10) fontSize := unit.Sp(10)
font := text.Font{} font := text.Font{}
/* /*
@@ -399,7 +399,7 @@ func TestEditorDimensions(t *testing.T) {
Locale: english, Locale: english,
} }
cache := text.NewCache(gofont.Collection()) cache := text.NewCache(gofont.Collection())
fontSize := unit.Px(10) fontSize := unit.Sp(10)
font := text.Font{} font := text.Font{}
dims := e.Layout(gtx, cache, font, fontSize, nil) dims := e.Layout(gtx, cache, font, fontSize, nil)
if dims.Size.X == 0 { if dims.Size.X == 0 {
@@ -446,7 +446,7 @@ func TestEditorCaretConsistency(t *testing.T) {
Locale: english, Locale: english,
} }
cache := text.NewCache(gofont.Collection()) cache := text.NewCache(gofont.Collection())
fontSize := unit.Px(10) fontSize := unit.Sp(10)
font := text.Font{} font := text.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{
@@ -539,7 +539,7 @@ func TestEditorMoveWord(t *testing.T) {
Locale: english, Locale: english,
} }
cache := text.NewCache(gofont.Collection()) cache := text.NewCache(gofont.Collection())
fontSize := unit.Px(10) fontSize := unit.Sp(10)
font := text.Font{} font := text.Font{}
e.SetText(t) e.SetText(t)
e.Layout(gtx, cache, font, fontSize, nil) e.Layout(gtx, cache, font, fontSize, nil)
@@ -644,7 +644,7 @@ func TestEditorInsert(t *testing.T) {
Locale: english, Locale: english,
} }
cache := text.NewCache(gofont.Collection()) cache := text.NewCache(gofont.Collection())
fontSize := unit.Px(10) fontSize := unit.Sp(10)
font := text.Font{} font := text.Font{}
e.SetText(t) e.SetText(t)
e.Layout(gtx, cache, font, fontSize, nil) e.Layout(gtx, cache, font, fontSize, nil)
@@ -734,7 +734,7 @@ func TestEditorDeleteWord(t *testing.T) {
Locale: english, Locale: english,
} }
cache := text.NewCache(gofont.Collection()) cache := text.NewCache(gofont.Collection())
fontSize := unit.Px(10) fontSize := unit.Sp(10)
font := text.Font{} font := text.Font{}
e.SetText(t) e.SetText(t)
e.Layout(gtx, cache, font, fontSize, nil) e.Layout(gtx, cache, font, fontSize, nil)
@@ -789,7 +789,7 @@ g 2 4 6 8 g
} }
cache := text.NewCache(gofont.Collection()) cache := text.NewCache(gofont.Collection())
font := text.Font{} font := text.Font{}
fontSize := unit.Px(10) fontSize := unit.Sp(10)
selected := func(start, end int) string { selected := func(start, end int) string {
// Layout once with no events; populate e.lines. // Layout once with no events; populate e.lines.
@@ -879,7 +879,7 @@ func TestSelectMove(t *testing.T) {
} }
cache := text.NewCache(gofont.Collection()) cache := text.NewCache(gofont.Collection())
font := text.Font{} font := text.Font{}
fontSize := unit.Px(10) fontSize := unit.Sp(10)
// Layout once to populate e.lines and get focus. // Layout once to populate e.lines and get focus.
gtx.Queue = newQueue(key.FocusEvent{Focus: true}) gtx.Queue = newQueue(key.FocusEvent{Focus: true})
+2 -2
View File
@@ -24,7 +24,7 @@ type Icon struct {
imgColor color.NRGBA imgColor color.NRGBA
} }
var defaultIconSize = unit.Dp(24) const defaultIconSize = unit.Dp(24)
// NewIcon returns a new Icon from IconVG data. // NewIcon returns a new Icon from IconVG data.
func NewIcon(data []byte) (*Icon, error) { func NewIcon(data []byte) (*Icon, error) {
@@ -39,7 +39,7 @@ func NewIcon(data []byte) (*Icon, error) {
func (ic *Icon) Layout(gtx layout.Context, color color.NRGBA) layout.Dimensions { func (ic *Icon) Layout(gtx layout.Context, color color.NRGBA) layout.Dimensions {
sz := gtx.Constraints.Min.X sz := gtx.Constraints.Min.X
if sz == 0 { if sz == 0 {
sz = gtx.Metric.Px(defaultIconSize) sz = gtx.Dp(defaultIconSize)
} }
size := gtx.Constraints.Constrain(image.Pt(sz, sz)) size := gtx.Constraints.Constrain(image.Pt(sz, sz))
defer clip.Rect{Max: size}.Push(gtx.Ops).Pop() defer clip.Rect{Max: size}.Push(gtx.Ops).Pop()
+1 -1
View File
@@ -39,7 +39,7 @@ func (im Image) Layout(gtx layout.Context) layout.Dimensions {
size := im.Src.Size() size := im.Src.Size()
wf, hf := float32(size.X), float32(size.Y) wf, hf := float32(size.X), float32(size.Y)
w, h := gtx.Px(unit.Dp(wf*scale)), gtx.Px(unit.Dp(hf*scale)) w, h := gtx.Dp(unit.Dp(wf*scale)), gtx.Dp(unit.Dp(hf*scale))
dims, trans := im.Fit.scale(gtx.Constraints, im.Position, layout.Dimensions{Size: image.Pt(w, h)}) dims, trans := im.Fit.scale(gtx.Constraints, im.Position, layout.Dimensions{Size: image.Pt(w, h)})
defer clip.Rect{Max: dims.Size}.Push(gtx.Ops).Pop() defer clip.Rect{Max: dims.Size}.Push(gtx.Ops).Pop()
+2 -2
View File
@@ -96,9 +96,9 @@ func (p1 screenPos) Less(p2 screenPos) bool {
return p1.Y < p2.Y || (p1.Y == p2.Y && p1.X < p2.X) return p1.Y < p2.Y || (p1.Y == p2.Y && p1.X < p2.X)
} }
func (l Label) Layout(gtx layout.Context, s text.Shaper, font text.Font, size unit.Value, txt string) layout.Dimensions { func (l Label) Layout(gtx layout.Context, s text.Shaper, font text.Font, size unit.Sp, txt string) layout.Dimensions {
cs := gtx.Constraints cs := gtx.Constraints
textSize := fixed.I(gtx.Px(size)) textSize := fixed.I(gtx.Sp(size))
lines := s.LayoutString(font, textSize, cs.Max.X, gtx.Locale, txt) lines := s.LayoutString(font, textSize, cs.Max.X, gtx.Locale, txt)
if max := l.MaxLines; max > 0 && len(lines) > max { if max := l.MaxLines; max > 0 && len(lines) > max {
lines = lines[:max] lines = lines[:max]
+13 -13
View File
@@ -23,9 +23,9 @@ type ButtonStyle struct {
// Color is the text color. // Color is the text color.
Color color.NRGBA Color color.NRGBA
Font text.Font Font text.Font
TextSize unit.Value TextSize unit.Sp
Background color.NRGBA Background color.NRGBA
CornerRadius unit.Value CornerRadius unit.Dp
Inset layout.Inset Inset layout.Inset
Button *widget.Clickable Button *widget.Clickable
shaper text.Shaper shaper text.Shaper
@@ -33,7 +33,7 @@ type ButtonStyle struct {
type ButtonLayoutStyle struct { type ButtonLayoutStyle struct {
Background color.NRGBA Background color.NRGBA
CornerRadius unit.Value CornerRadius unit.Dp
Button *widget.Clickable Button *widget.Clickable
} }
@@ -43,7 +43,7 @@ type IconButtonStyle struct {
Color color.NRGBA Color color.NRGBA
Icon *widget.Icon Icon *widget.Icon
// Size is the icon size. // Size is the icon size.
Size unit.Value Size unit.Dp
Inset layout.Inset Inset layout.Inset
Button *widget.Clickable Button *widget.Clickable
Description string Description string
@@ -53,12 +53,12 @@ func Button(th *Theme, button *widget.Clickable, txt string) ButtonStyle {
return ButtonStyle{ return ButtonStyle{
Text: txt, Text: txt,
Color: th.Palette.ContrastFg, Color: th.Palette.ContrastFg,
CornerRadius: unit.Dp(4), CornerRadius: 4,
Background: th.Palette.ContrastBg, Background: th.Palette.ContrastBg,
TextSize: th.TextSize.Scale(14.0 / 16.0), TextSize: th.TextSize * 14.0 / 16.0,
Inset: layout.Inset{ Inset: layout.Inset{
Top: unit.Dp(10), Bottom: unit.Dp(10), Top: 10, Bottom: 10,
Left: unit.Dp(12), Right: unit.Dp(12), Left: 12, Right: 12,
}, },
Button: button, Button: button,
shaper: th.Shaper, shaper: th.Shaper,
@@ -69,7 +69,7 @@ func ButtonLayout(th *Theme, button *widget.Clickable) ButtonLayoutStyle {
return ButtonLayoutStyle{ return ButtonLayoutStyle{
Button: button, Button: button,
Background: th.Palette.ContrastBg, Background: th.Palette.ContrastBg,
CornerRadius: unit.Dp(4), CornerRadius: 4,
} }
} }
@@ -78,8 +78,8 @@ func IconButton(th *Theme, button *widget.Clickable, icon *widget.Icon, descript
Background: th.Palette.ContrastBg, Background: th.Palette.ContrastBg,
Color: th.Palette.ContrastFg, Color: th.Palette.ContrastFg,
Icon: icon, Icon: icon,
Size: unit.Dp(24), Size: 24,
Inset: layout.UniformInset(unit.Dp(12)), Inset: layout.UniformInset(12),
Button: button, Button: button,
Description: description, Description: description,
} }
@@ -129,7 +129,7 @@ func (b ButtonLayoutStyle) Layout(gtx layout.Context, w layout.Widget) layout.Di
semantic.Button.Add(gtx.Ops) semantic.Button.Add(gtx.Ops)
return layout.Stack{Alignment: layout.Center}.Layout(gtx, return layout.Stack{Alignment: layout.Center}.Layout(gtx,
layout.Expanded(func(gtx layout.Context) layout.Dimensions { layout.Expanded(func(gtx layout.Context) layout.Dimensions {
rr := gtx.Px(b.CornerRadius) rr := gtx.Dp(b.CornerRadius)
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop() defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop()
background := b.Background background := b.Background
switch { switch {
@@ -178,7 +178,7 @@ func (b IconButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
}), }),
layout.Stacked(func(gtx layout.Context) layout.Dimensions { layout.Stacked(func(gtx layout.Context) layout.Dimensions {
return b.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { return b.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
size := gtx.Px(b.Size) size := gtx.Dp(b.Size)
if b.Icon != nil { if b.Icon != nil {
gtx.Constraints.Min = image.Point{X: size} gtx.Constraints.Min = image.Point{X: size}
b.Icon.Layout(gtx, b.Color) b.Icon.Layout(gtx, b.Color)
+6 -6
View File
@@ -19,9 +19,9 @@ type checkable struct {
Label string Label string
Color color.NRGBA Color color.NRGBA
Font text.Font Font text.Font
TextSize unit.Value TextSize unit.Sp
IconColor color.NRGBA IconColor color.NRGBA
Size unit.Value Size unit.Dp
shaper text.Shaper shaper text.Shaper
checkedStateIcon *widget.Icon checkedStateIcon *widget.Icon
uncheckedStateIcon *widget.Icon uncheckedStateIcon *widget.Icon
@@ -39,7 +39,7 @@ func (c *checkable) layout(gtx layout.Context, checked, hovered bool) layout.Dim
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return layout.Stack{Alignment: layout.Center}.Layout(gtx, return layout.Stack{Alignment: layout.Center}.Layout(gtx,
layout.Stacked(func(gtx layout.Context) layout.Dimensions { layout.Stacked(func(gtx layout.Context) layout.Dimensions {
size := gtx.Px(c.Size) * 4 / 3 size := gtx.Dp(c.Size) * 4 / 3
dims := layout.Dimensions{ dims := layout.Dimensions{
Size: image.Point{X: size, Y: size}, Size: image.Point{X: size, Y: size},
} }
@@ -55,8 +55,8 @@ func (c *checkable) layout(gtx layout.Context, checked, hovered bool) layout.Dim
return dims return dims
}), }),
layout.Stacked(func(gtx layout.Context) layout.Dimensions { layout.Stacked(func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(2)).Layout(gtx, func(gtx layout.Context) layout.Dimensions { return layout.UniformInset(2).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
size := gtx.Px(c.Size) size := gtx.Dp(c.Size)
col := c.IconColor col := c.IconColor
if gtx.Queue == nil { if gtx.Queue == nil {
col = f32color.Disabled(col) col = f32color.Disabled(col)
@@ -72,7 +72,7 @@ func (c *checkable) layout(gtx layout.Context, checked, hovered bool) layout.Dim
}), }),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(2)).Layout(gtx, func(gtx layout.Context) layout.Dimensions { return layout.UniformInset(2).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
paint.ColorOp{Color: c.Color}.Add(gtx.Ops) paint.ColorOp{Color: c.Color}.Add(gtx.Ops)
return widget.Label{}.Layout(gtx, c.shaper, c.Font, c.TextSize, c.Label) return widget.Label{}.Layout(gtx, c.shaper, c.Font, c.TextSize, c.Label)
}) })
+2 -3
View File
@@ -5,7 +5,6 @@ package material
import ( import (
"gioui.org/io/semantic" "gioui.org/io/semantic"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/unit"
"gioui.org/widget" "gioui.org/widget"
) )
@@ -21,8 +20,8 @@ func CheckBox(th *Theme, checkBox *widget.Bool, label string) CheckBoxStyle {
Label: label, Label: label,
Color: th.Palette.Fg, Color: th.Palette.Fg,
IconColor: th.Palette.ContrastBg, IconColor: th.Palette.ContrastBg,
TextSize: th.TextSize.Scale(14.0 / 16.0), TextSize: th.TextSize * 14.0 / 16.0,
Size: unit.Dp(26), Size: 26,
shaper: th.Shaper, shaper: th.Shaper,
checkedStateIcon: th.Icon.CheckBoxChecked, checkedStateIcon: th.Icon.CheckBoxChecked,
uncheckedStateIcon: th.Icon.CheckBoxUnchecked, uncheckedStateIcon: th.Icon.CheckBoxUnchecked,
+14 -14
View File
@@ -53,7 +53,7 @@ func (d *DecorationsStyle) Layout(gtx layout.Context) layout.Dimensions {
func (d *DecorationsStyle) layoutDecorations(gtx layout.Context) layout.Dimensions { func (d *DecorationsStyle) layoutDecorations(gtx layout.Context) layout.Dimensions {
gtx.Constraints.Min.Y = 0 gtx.Constraints.Min.Y = 0
inset := layout.UniformInset(unit.Dp(10)) inset := layout.UniformInset(10)
return layout.Flex{ return layout.Flex{
Axis: layout.Horizontal, Axis: layout.Horizontal,
Alignment: layout.Middle, Alignment: layout.Middle,
@@ -116,7 +116,7 @@ func (d *DecorationsStyle) layoutDecorations(gtx layout.Context) layout.Dimensio
) )
} }
var ( const (
winIconSize = unit.Dp(20) winIconSize = unit.Dp(20)
winIconMargin = unit.Dp(4) winIconMargin = unit.Dp(4)
winIconStroke = unit.Dp(2) winIconStroke = unit.Dp(2)
@@ -124,10 +124,10 @@ var (
// minimizeWindows draws a line icon representing the minimize action. // minimizeWindows draws a line icon representing the minimize action.
func minimizeWindow(gtx layout.Context) layout.Dimensions { func minimizeWindow(gtx layout.Context) layout.Dimensions {
size := gtx.Px(winIconSize) size := gtx.Dp(winIconSize)
size32 := float32(size) size32 := float32(size)
margin := float32(gtx.Px(winIconMargin)) margin := float32(gtx.Dp(winIconMargin))
width := float32(gtx.Px(winIconStroke)) width := float32(gtx.Dp(winIconStroke))
var p clip.Path var p clip.Path
p.Begin(gtx.Ops) p.Begin(gtx.Ops)
p.MoveTo(f32.Point{X: margin, Y: size32 - margin}) p.MoveTo(f32.Point{X: margin, Y: size32 - margin})
@@ -143,9 +143,9 @@ func minimizeWindow(gtx layout.Context) layout.Dimensions {
// maximizeWindow draws a rectangle representing the maximize action. // maximizeWindow draws a rectangle representing the maximize action.
func maximizeWindow(gtx layout.Context) layout.Dimensions { func maximizeWindow(gtx layout.Context) layout.Dimensions {
size := gtx.Px(winIconSize) size := gtx.Dp(winIconSize)
margin := gtx.Px(winIconMargin) margin := gtx.Dp(winIconMargin)
width := gtx.Px(winIconStroke) width := gtx.Dp(winIconStroke)
r := clip.RRect{ r := clip.RRect{
Rect: image.Rect(margin, margin, size-margin, size-margin), Rect: image.Rect(margin, margin, size-margin, size-margin),
} }
@@ -166,9 +166,9 @@ func maximizeWindow(gtx layout.Context) layout.Dimensions {
// maximizedWindow draws interleaved rectangles representing the un-maximize action. // maximizedWindow draws interleaved rectangles representing the un-maximize action.
func maximizedWindow(gtx layout.Context) layout.Dimensions { func maximizedWindow(gtx layout.Context) layout.Dimensions {
size := gtx.Px(winIconSize) size := gtx.Dp(winIconSize)
margin := gtx.Px(winIconMargin) margin := gtx.Dp(winIconMargin)
width := gtx.Px(winIconStroke) width := gtx.Dp(winIconStroke)
r := clip.RRect{ r := clip.RRect{
Rect: image.Rect(margin, margin, size-2*margin, size-2*margin), Rect: image.Rect(margin, margin, size-2*margin, size-2*margin),
} }
@@ -192,10 +192,10 @@ func maximizedWindow(gtx layout.Context) layout.Dimensions {
// closeWindow draws a cross representing the close action. // closeWindow draws a cross representing the close action.
func closeWindow(gtx layout.Context) layout.Dimensions { func closeWindow(gtx layout.Context) layout.Dimensions {
size := gtx.Px(winIconSize) size := gtx.Dp(winIconSize)
size32 := float32(size) size32 := float32(size)
margin := float32(gtx.Px(winIconMargin)) margin := float32(gtx.Dp(winIconMargin))
width := float32(gtx.Px(winIconStroke)) width := float32(gtx.Dp(winIconStroke))
var p clip.Path var p clip.Path
p.Begin(gtx.Ops) p.Begin(gtx.Ops)
p.MoveTo(f32.Point{X: margin, Y: margin}) p.MoveTo(f32.Point{X: margin, Y: margin})
+1 -1
View File
@@ -17,7 +17,7 @@ import (
type EditorStyle struct { type EditorStyle struct {
Font text.Font Font text.Font
TextSize unit.Value TextSize unit.Sp
// Color is the text color. // Color is the text color.
Color color.NRGBA Color color.NRGBA
// Hint contains the text displayed when the editor is empty. // Hint contains the text displayed when the editor is empty.
+13 -13
View File
@@ -22,47 +22,47 @@ type LabelStyle struct {
// MaxLines limits the number of lines. Zero means no limit. // MaxLines limits the number of lines. Zero means no limit.
MaxLines int MaxLines int
Text string Text string
TextSize unit.Value TextSize unit.Sp
shaper text.Shaper shaper text.Shaper
} }
func H1(th *Theme, txt string) LabelStyle { func H1(th *Theme, txt string) LabelStyle {
label := Label(th, th.TextSize.Scale(96.0/16.0), txt) label := Label(th, th.TextSize*96.0/16.0, txt)
label.Font.Weight = text.Light label.Font.Weight = text.Light
return label return label
} }
func H2(th *Theme, txt string) LabelStyle { func H2(th *Theme, txt string) LabelStyle {
label := Label(th, th.TextSize.Scale(60.0/16.0), txt) label := Label(th, th.TextSize*60.0/16.0, txt)
label.Font.Weight = text.Light label.Font.Weight = text.Light
return label return label
} }
func H3(th *Theme, txt string) LabelStyle { func H3(th *Theme, txt string) LabelStyle {
return Label(th, th.TextSize.Scale(48.0/16.0), txt) return Label(th, th.TextSize*48.0/16.0, txt)
} }
func H4(th *Theme, txt string) LabelStyle { func H4(th *Theme, txt string) LabelStyle {
return Label(th, th.TextSize.Scale(34.0/16.0), txt) return Label(th, th.TextSize*34.0/16.0, txt)
} }
func H5(th *Theme, txt string) LabelStyle { func H5(th *Theme, txt string) LabelStyle {
return Label(th, th.TextSize.Scale(24.0/16.0), txt) return Label(th, th.TextSize*24.0/16.0, txt)
} }
func H6(th *Theme, txt string) LabelStyle { func H6(th *Theme, txt string) LabelStyle {
label := Label(th, th.TextSize.Scale(20.0/16.0), txt) label := Label(th, th.TextSize*20.0/16.0, txt)
label.Font.Weight = text.Medium label.Font.Weight = text.Medium
return label return label
} }
func Subtitle1(th *Theme, txt string) LabelStyle { func Subtitle1(th *Theme, txt string) LabelStyle {
return Label(th, th.TextSize.Scale(16.0/16.0), txt) return Label(th, th.TextSize*16.0/16.0, txt)
} }
func Subtitle2(th *Theme, txt string) LabelStyle { func Subtitle2(th *Theme, txt string) LabelStyle {
label := Label(th, th.TextSize.Scale(14.0/16.0), txt) label := Label(th, th.TextSize*14.0/16.0, txt)
label.Font.Weight = text.Medium label.Font.Weight = text.Medium
return label return label
} }
@@ -72,18 +72,18 @@ func Body1(th *Theme, txt string) LabelStyle {
} }
func Body2(th *Theme, txt string) LabelStyle { func Body2(th *Theme, txt string) LabelStyle {
return Label(th, th.TextSize.Scale(14.0/16.0), txt) return Label(th, th.TextSize*14.0/16.0, txt)
} }
func Caption(th *Theme, txt string) LabelStyle { func Caption(th *Theme, txt string) LabelStyle {
return Label(th, th.TextSize.Scale(12.0/16.0), txt) return Label(th, th.TextSize*12.0/16.0, txt)
} }
func Overline(th *Theme, txt string) LabelStyle { func Overline(th *Theme, txt string) LabelStyle {
return Label(th, th.TextSize.Scale(10.0/16.0), txt) return Label(th, th.TextSize*10.0/16.0, txt)
} }
func Label(th *Theme, size unit.Value, txt string) LabelStyle { func Label(th *Theme, size unit.Sp, txt string) LabelStyle {
return LabelStyle{ return LabelStyle{
Text: txt, Text: txt,
Color: th.Palette.Fg, Color: th.Palette.Fg,
+16 -16
View File
@@ -50,7 +50,7 @@ type ScrollTrackStyle struct {
// MajorPadding and MinorPadding along the major and minor axis of the // MajorPadding and MinorPadding along the major and minor axis of the
// scrollbar's track. This is used to keep the scrollbar from touching // scrollbar's track. This is used to keep the scrollbar from touching
// the edges of the content area. // the edges of the content area.
MajorPadding, MinorPadding unit.Value MajorPadding, MinorPadding unit.Dp
// Color of the track background. // Color of the track background.
Color color.NRGBA Color color.NRGBA
} }
@@ -59,16 +59,16 @@ type ScrollTrackStyle struct {
type ScrollIndicatorStyle struct { type ScrollIndicatorStyle struct {
// MajorMinLen is the smallest that the scroll indicator is allowed to // MajorMinLen is the smallest that the scroll indicator is allowed to
// be along the major axis. // be along the major axis.
MajorMinLen unit.Value MajorMinLen unit.Dp
// MinorWidth is the width of the scroll indicator across the minor axis. // MinorWidth is the width of the scroll indicator across the minor axis.
MinorWidth unit.Value MinorWidth unit.Dp
// Color and HoverColor are the normal and hovered colors of the scroll // Color and HoverColor are the normal and hovered colors of the scroll
// indicator. // indicator.
Color, HoverColor color.NRGBA Color, HoverColor color.NRGBA
// CornerRadius is the corner radius of the rectangular indicator. 0 // CornerRadius is the corner radius of the rectangular indicator. 0
// will produce square corners. 0.5*MinorWidth will produce perfectly // will produce square corners. 0.5*MinorWidth will produce perfectly
// round corners. // round corners.
CornerRadius unit.Value CornerRadius unit.Dp
} }
// ScrollbarStyle configures the presentation of a scrollbar. // ScrollbarStyle configures the presentation of a scrollbar.
@@ -89,13 +89,13 @@ func Scrollbar(th *Theme, state *widget.Scrollbar) ScrollbarStyle {
return ScrollbarStyle{ return ScrollbarStyle{
Scrollbar: state, Scrollbar: state,
Track: ScrollTrackStyle{ Track: ScrollTrackStyle{
MajorPadding: unit.Dp(2), MajorPadding: 2,
MinorPadding: unit.Dp(2), MinorPadding: 2,
}, },
Indicator: ScrollIndicatorStyle{ Indicator: ScrollIndicatorStyle{
MajorMinLen: unit.Dp(8), MajorMinLen: 8,
MinorWidth: unit.Dp(6), MinorWidth: 6,
CornerRadius: unit.Dp(3), CornerRadius: 3,
Color: lightFg, Color: lightFg,
HoverColor: darkFg, HoverColor: darkFg,
}, },
@@ -104,8 +104,8 @@ func Scrollbar(th *Theme, state *widget.Scrollbar) ScrollbarStyle {
// Width returns the minor axis width of the scrollbar in its current // Width returns the minor axis width of the scrollbar in its current
// configuration (taking padding for the scroll track into account). // configuration (taking padding for the scroll track into account).
func (s ScrollbarStyle) Width(metric unit.Metric) unit.Value { func (s ScrollbarStyle) Width() unit.Dp {
return unit.Add(metric, s.Indicator.MinorWidth, s.Track.MinorPadding, s.Track.MinorPadding) return s.Indicator.MinorWidth + s.Track.MinorPadding + s.Track.MinorPadding
} }
// Layout the scrollbar. // Layout the scrollbar.
@@ -119,7 +119,7 @@ func (s ScrollbarStyle) Layout(gtx layout.Context, axis layout.Axis, viewportSta
convert := axis.Convert convert := axis.Convert
maxMajorAxis := convert(gtx.Constraints.Max).X maxMajorAxis := convert(gtx.Constraints.Max).X
gtx.Constraints.Min.X = maxMajorAxis gtx.Constraints.Min.X = maxMajorAxis
gtx.Constraints.Min.Y = gtx.Px(s.Width(gtx.Metric)) gtx.Constraints.Min.Y = gtx.Dp(s.Width())
gtx.Constraints.Min = convert(gtx.Constraints.Min) gtx.Constraints.Min = convert(gtx.Constraints.Min)
gtx.Constraints.Max = gtx.Constraints.Min gtx.Constraints.Max = gtx.Constraints.Min
@@ -179,15 +179,15 @@ func (s ScrollbarStyle) layout(gtx layout.Context, axis layout.Axis, viewportSta
trackLen := gtx.Constraints.Min.X trackLen := gtx.Constraints.Min.X
viewStart := int(math.Round(float64(viewportStart) * float64(trackLen))) viewStart := int(math.Round(float64(viewportStart) * float64(trackLen)))
viewEnd := int(math.Round(float64(viewportEnd) * float64(trackLen))) viewEnd := int(math.Round(float64(viewportEnd) * float64(trackLen)))
indicatorLen := max(viewEnd-viewStart, gtx.Px(s.Indicator.MajorMinLen)) indicatorLen := max(viewEnd-viewStart, gtx.Dp(s.Indicator.MajorMinLen))
if viewStart+indicatorLen > trackLen { if viewStart+indicatorLen > trackLen {
viewStart = trackLen - indicatorLen viewStart = trackLen - indicatorLen
} }
indicatorDims := axis.Convert(image.Point{ indicatorDims := axis.Convert(image.Point{
X: indicatorLen, X: indicatorLen,
Y: gtx.Px(s.Indicator.MinorWidth), Y: gtx.Dp(s.Indicator.MinorWidth),
}) })
radius := gtx.Px(s.Indicator.CornerRadius) radius := gtx.Dp(s.Indicator.CornerRadius)
// Lay out the indicator. // Lay out the indicator.
offset := axis.Convert(image.Pt(viewStart, 0)) offset := axis.Convert(image.Pt(viewStart, 0))
@@ -247,7 +247,7 @@ func (l ListStyle) Layout(gtx layout.Context, length int, w layout.ListElement)
originalConstraints := gtx.Constraints originalConstraints := gtx.Constraints
// Determine how much space the scrollbar occupies. // Determine how much space the scrollbar occupies.
barWidth := gtx.Px(l.Width(gtx.Metric)) barWidth := gtx.Dp(l.Width())
if l.AnchorStrategy == Occupy { if l.AnchorStrategy == Occupy {
+2 -2
View File
@@ -38,7 +38,7 @@ func TestListAnchorStrategies(t *testing.T) {
} }
return layout.Dimensions{Size: image.Point{ return layout.Dimensions{Size: image.Point{
X: gtx.Constraints.Max.X, X: gtx.Constraints.Max.X,
Y: gtx.Px(unit.Dp(20)), Y: gtx.Dp(20),
}} }}
} }
@@ -47,7 +47,7 @@ func TestListAnchorStrategies(t *testing.T) {
elements := 100 elements := 100
th := material.NewTheme(gofont.Collection()) th := material.NewTheme(gofont.Collection())
materialList := material.List(th, &list) materialList := material.List(th, &list)
indicatorWidth := gtx.Px(materialList.Width(gtx.Metric)) indicatorWidth := gtx.Dp(materialList.Width())
materialList.AnchorStrategy = material.Occupy materialList.AnchorStrategy = material.Occupy
occupyDims := materialList.Layout(gtx, elements, space) occupyDims := materialList.Layout(gtx, elements, space)
+1 -2
View File
@@ -13,7 +13,6 @@ import (
"gioui.org/op" "gioui.org/op"
"gioui.org/op/clip" "gioui.org/op/clip"
"gioui.org/op/paint" "gioui.org/op/paint"
"gioui.org/unit"
) )
type LoaderStyle struct { type LoaderStyle struct {
@@ -32,7 +31,7 @@ func (l LoaderStyle) Layout(gtx layout.Context) layout.Dimensions {
diam = minY diam = minY
} }
if diam == 0 { if diam == 0 {
diam = gtx.Px(unit.Dp(24)) diam = gtx.Dp(24)
} }
sz := gtx.Constraints.Constrain(image.Pt(diam, diam)) sz := gtx.Constraints.Constrain(image.Pt(diam, diam))
radius := sz.X / 2 radius := sz.X / 2
+3 -3
View File
@@ -29,10 +29,10 @@ func ProgressBar(th *Theme, progress float32) ProgressBarStyle {
func (p ProgressBarStyle) Layout(gtx layout.Context) layout.Dimensions { func (p ProgressBarStyle) Layout(gtx layout.Context) layout.Dimensions {
shader := func(width int, color color.NRGBA) layout.Dimensions { shader := func(width int, color color.NRGBA) layout.Dimensions {
var maxHeight = unit.Dp(4) const maxHeight = unit.Dp(4)
rr := gtx.Px(unit.Dp(2)) rr := gtx.Dp(2)
d := image.Point{X: width, Y: gtx.Px(maxHeight)} d := image.Point{X: width, Y: gtx.Dp(maxHeight)}
defer clip.UniformRRect(image.Rectangle{Max: image.Pt(width, d.Y)}, rr).Push(gtx.Ops).Pop() defer clip.UniformRRect(image.Rectangle{Max: image.Pt(width, d.Y)}, rr).Push(gtx.Ops).Pop()
paint.ColorOp{Color: color}.Add(gtx.Ops) paint.ColorOp{Color: color}.Add(gtx.Ops)
+1 -2
View File
@@ -10,7 +10,6 @@ import (
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
"gioui.org/op/paint" "gioui.org/op/paint"
"gioui.org/unit"
) )
type ProgressCircleStyle struct { type ProgressCircleStyle struct {
@@ -31,7 +30,7 @@ func (p ProgressCircleStyle) Layout(gtx layout.Context) layout.Dimensions {
diam = minY diam = minY
} }
if diam == 0 { if diam == 0 {
diam = gtx.Px(unit.Dp(24)) diam = gtx.Dp(24)
} }
sz := gtx.Constraints.Constrain(image.Pt(diam, diam)) sz := gtx.Constraints.Constrain(image.Pt(diam, diam))
radius := sz.X / 2 radius := sz.X / 2
+2 -3
View File
@@ -5,7 +5,6 @@ package material
import ( import (
"gioui.org/io/semantic" "gioui.org/io/semantic"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/unit"
"gioui.org/widget" "gioui.org/widget"
) )
@@ -25,8 +24,8 @@ func RadioButton(th *Theme, group *widget.Enum, key, label string) RadioButtonSt
Color: th.Palette.Fg, Color: th.Palette.Fg,
IconColor: th.Palette.ContrastBg, IconColor: th.Palette.ContrastBg,
TextSize: th.TextSize.Scale(14.0 / 16.0), TextSize: th.TextSize * 14.0 / 16.0,
Size: unit.Dp(26), Size: 26,
shaper: th.Shaper, shaper: th.Shaper,
checkedStateIcon: th.Icon.RadioChecked, checkedStateIcon: th.Icon.RadioChecked,
uncheckedStateIcon: th.Icon.RadioUnchecked, uncheckedStateIcon: th.Icon.RadioUnchecked,
+4 -4
View File
@@ -31,19 +31,19 @@ type SliderStyle struct {
Color color.NRGBA Color color.NRGBA
Float *widget.Float Float *widget.Float
FingerSize unit.Value FingerSize unit.Dp
} }
func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions { func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
thumbRadius := gtx.Px(unit.Dp(6)) thumbRadius := gtx.Dp(6)
trackWidth := gtx.Px(unit.Dp(2)) trackWidth := gtx.Dp(2)
axis := s.Float.Axis axis := s.Float.Axis
// Keep a minimum length so that the track is always visible. // Keep a minimum length so that the track is always visible.
minLength := thumbRadius + 3*thumbRadius + thumbRadius minLength := thumbRadius + 3*thumbRadius + thumbRadius
// Try to expand to finger size, but only if the constraints // Try to expand to finger size, but only if the constraints
// allow for it. // allow for it.
touchSizePx := min(gtx.Px(s.FingerSize), axis.Convert(gtx.Constraints.Max).Y) touchSizePx := min(gtx.Dp(s.FingerSize), axis.Convert(gtx.Constraints.Max).Y)
sizeMain := max(axis.Convert(gtx.Constraints.Min).X, minLength) sizeMain := max(axis.Convert(gtx.Constraints.Min).X, minLength)
sizeCross := max(2*thumbRadius, touchSizePx) sizeCross := max(2*thumbRadius, touchSizePx)
size := axis.Convert(image.Pt(sizeMain, sizeCross)) size := axis.Convert(image.Pt(sizeMain, sizeCross))
+6 -7
View File
@@ -12,7 +12,6 @@ import (
"gioui.org/op" "gioui.org/op"
"gioui.org/op/clip" "gioui.org/op/clip"
"gioui.org/op/paint" "gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget" "gioui.org/widget"
) )
@@ -40,9 +39,9 @@ func Switch(th *Theme, swtch *widget.Bool, description string) SwitchStyle {
// Layout updates the switch and displays it. // Layout updates the switch and displays it.
func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions { func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
trackWidth := gtx.Px(unit.Dp(36)) trackWidth := gtx.Dp(36)
trackHeight := gtx.Px(unit.Dp(16)) trackHeight := gtx.Dp(16)
thumbSize := gtx.Px(unit.Dp(20)) thumbSize := gtx.Dp(20)
trackOff := (thumbSize - trackHeight) / 2 trackOff := (thumbSize - trackHeight) / 2
// Draw track. // Draw track.
@@ -67,7 +66,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
t.Pop() t.Pop()
// Draw thumb ink. // Draw thumb ink.
inkSize := gtx.Px(unit.Dp(44)) inkSize := gtx.Dp(44)
rr := inkSize / 2 rr := inkSize / 2
inkOff := image.Point{ inkOff := image.Point{
X: trackWidth/2 - rr, X: trackWidth/2 - rr,
@@ -107,13 +106,13 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
// Draw thumb shadow, a translucent disc slightly larger than the // Draw thumb shadow, a translucent disc slightly larger than the
// thumb itself. // thumb itself.
// Center shadow horizontally and slightly adjust its Y. // Center shadow horizontally and slightly adjust its Y.
paint.FillShape(gtx.Ops, argb(0x55000000), circle(thumbRadius, thumbRadius+gtx.Px(unit.Dp(.25)), thumbRadius+1)) paint.FillShape(gtx.Ops, argb(0x55000000), circle(thumbRadius, thumbRadius+gtx.Dp(.25), thumbRadius+1))
// Draw thumb. // Draw thumb.
paint.FillShape(gtx.Ops, col, circle(thumbRadius, thumbRadius, thumbRadius)) paint.FillShape(gtx.Ops, col, circle(thumbRadius, thumbRadius, thumbRadius))
// Set up click area. // Set up click area.
clickSize := gtx.Px(unit.Dp(40)) clickSize := gtx.Dp(40)
clickOff := image.Point{ clickOff := image.Point{
X: (thumbSize - clickSize) / 2, X: (thumbSize - clickSize) / 2,
Y: (trackHeight-clickSize)/2 + trackOff, Y: (trackHeight-clickSize)/2 + trackOff,
+4 -4
View File
@@ -34,7 +34,7 @@ type Palette struct {
type Theme struct { type Theme struct {
Shaper text.Shaper Shaper text.Shaper
Palette Palette
TextSize unit.Value TextSize unit.Sp
Icon struct { Icon struct {
CheckBoxChecked *widget.Icon CheckBoxChecked *widget.Icon
CheckBoxUnchecked *widget.Icon CheckBoxUnchecked *widget.Icon
@@ -43,7 +43,7 @@ type Theme struct {
} }
// FingerSize is the minimum touch target size. // FingerSize is the minimum touch target size.
FingerSize unit.Value FingerSize unit.Dp
} }
func NewTheme(fontCollection []text.FontFace) *Theme { func NewTheme(fontCollection []text.FontFace) *Theme {
@@ -56,7 +56,7 @@ func NewTheme(fontCollection []text.FontFace) *Theme {
ContrastBg: rgb(0x3f51b5), ContrastBg: rgb(0x3f51b5),
ContrastFg: rgb(0xffffff), ContrastFg: rgb(0xffffff),
} }
t.TextSize = unit.Sp(16) t.TextSize = 16
t.Icon.CheckBoxChecked = mustIcon(widget.NewIcon(icons.ToggleCheckBox)) t.Icon.CheckBoxChecked = mustIcon(widget.NewIcon(icons.ToggleCheckBox))
t.Icon.CheckBoxUnchecked = mustIcon(widget.NewIcon(icons.ToggleCheckBoxOutlineBlank)) t.Icon.CheckBoxUnchecked = mustIcon(widget.NewIcon(icons.ToggleCheckBoxOutlineBlank))
@@ -64,7 +64,7 @@ func NewTheme(fontCollection []text.FontFace) *Theme {
t.Icon.RadioUnchecked = mustIcon(widget.NewIcon(icons.ToggleRadioButtonUnchecked)) t.Icon.RadioUnchecked = mustIcon(widget.NewIcon(icons.ToggleRadioButtonUnchecked))
// 38dp is on the lower end of possible finger size. // 38dp is on the lower end of possible finger size.
t.FingerSize = unit.Dp(38) t.FingerSize = 38
return t return t
} }