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
+18 -114
View File
@@ -2,9 +2,7 @@
/*
Package unit implements device independent units and values.
A Value is a value with a Unit attached.
Package unit implements device independent units.
Device independent pixel, or dp, is the unit for sizes independent of
the underlying display device.
@@ -23,19 +21,9 @@ values.
package unit
import (
"fmt"
"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
// value represents a 1-to-1 scale from dp, sp to pixels.
type Metric struct {
@@ -45,113 +33,29 @@ type Metric struct {
PxPerSp float32
}
const (
// UnitPx represent device pixels in the resolution of
// the underlying display.
UnitPx Unit = iota
// UnitDp represents device independent pixels. 1 dp will
type (
// Dp represents device independent pixels. 1 dp will
// have the same apparent size across platforms and
// display resolutions.
UnitDp
// UnitSp is like UnitDp but for font sizes.
UnitSp
Dp float32
// Sp is like UnitDp but for font sizes.
Sp float32
)
// Px returns the Value for v device pixels.
func Px(v float32) Value {
return Value{V: v, U: UnitPx}
}
// 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")
// Dp converts v to pixels, rounded to the nearest integer value.
func (c Metric) Dp(v Dp) int {
s := c.PxPerDp
if s == 0. {
s = 1.
}
return int(math.Round(float64(s) * float64(v)))
}
// Add a list of Values.
func Add(c Metric, values ...Value) Value {
var sum Value
for _, v := range values {
sum, v = compatible(c, sum, v)
sum.V += v.V
// Sp converts v to pixels, rounded to the nearest integer value.
func (c Metric) Sp(v Sp) int {
s := c.PxPerSp
if s == 0. {
s = 1.
}
return sum
}
// 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)))
return int(math.Round(float64(s) * float64(v)))
}