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
gtx := layout.Context{Ops: new(op.Ops), Queue: &r}
// 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)
var state editorState
@@ -103,7 +103,7 @@ func FuzzIME(f *testing.F) {
}
}
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)
newState := r.EditorState()
// We don't track caret position.
+10 -8
View File
@@ -154,7 +154,7 @@ type window struct {
dpi int
fontScale float32
insets system.Insets
insets image.Rectangle
stage system.Stage
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
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.insets = system.Insets{
Top: unit.Px(float32(top)),
Bottom: unit.Px(float32(bottom)),
Left: unit.Px(float32(left)),
Right: unit.Px(float32(right)),
}
w.insets = image.Rect(int(left), int(top), int(right), int(bottom))
if w.stage >= system.StageRunning {
w.draw(env, true)
}
@@ -830,11 +825,18 @@ func (w *window) draw(env *C.JNIEnv, sync bool) {
}
const inchPrDp = 1.0 / 160
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{
FrameEvent: system.FrameEvent{
Now: time.Now(),
Size: w.config.Size,
Insets: w.insets,
Insets: insets,
Metric: unit.Metric{
PxPerDp: 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})
}
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{
FrameEvent: system.FrameEvent{
Now: time.Now(),
@@ -158,15 +163,12 @@ func (w *window) draw(sync bool) {
Y: int(params.height + .5),
},
Insets: system.Insets{
Top: unit.Px(float32(params.top)),
Bottom: unit.Px(float32(params.bottom)),
Left: unit.Px(float32(params.left)),
Right: unit.Px(float32(params.right)),
},
Metric: unit.Metric{
PxPerDp: float32(params.dpi) * inchPrDp,
PxPerSp: float32(params.sdpi) * inchPrDp,
Top: unit.Dp(params.top) * dppp,
Bottom: unit.Dp(params.bottom) * dppp,
Left: unit.Dp(params.left) * dppp,
Right: unit.Dp(params.right) * dppp,
},
Metric: m,
},
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) {
invscale := unit.Dp(1. / w.scale)
return image.Pt(w.config.Size.X, w.config.Size.Y),
system.Insets{
Bottom: unit.Px(w.inset.Y),
Right: unit.Px(w.inset.X),
Bottom: unit.Dp(w.inset.Y) * invscale,
Right: unit.Dp(w.inset.X) * invscale,
}, unit.Metric{
PxPerDp: w.scale,
PxPerSp: w.scale,
+22 -22
View File
@@ -138,7 +138,7 @@ var ackEvent event.Event
// iOS, Android, WebAssembly.
func NewWindow(options ...Option) *Window {
defaultOptions := []Option{
Size(unit.Dp(800), unit.Dp(600)),
Size(800, 600),
Title("Gio"),
}
options = append(defaultOptions, options...)
@@ -546,8 +546,8 @@ func (w *Window) moveFocus(dir router.FocusDirection, d driver) {
default:
return
}
const scrollABit = 50
dist := v.Mul(w.metric.Px(unit.Dp(scrollABit)))
const scrollABit = unit.Dp(50)
dist := v.Mul(int(w.metric.Dp(scrollABit)))
w.queue.q.ScrollFocus(dist)
}
w.setNextFrame(time.Time{})
@@ -805,12 +805,12 @@ func (w *Window) processEvent(d driver, e event.Event) bool {
wrapper.Reset()
viewport := image.Rectangle{
Min: image.Point{
X: e2.Metric.Px(e2.Insets.Left),
Y: e2.Metric.Px(e2.Insets.Top),
X: e2.Metric.Dp(e2.Insets.Left),
Y: e2.Metric.Dp(e2.Insets.Top),
},
Max: image.Point{
X: e2.Size.X - e2.Metric.Px(e2.Insets.Right),
Y: e2.Size.Y - e2.Metric.Px(e2.Insets.Bottom),
X: e2.Size.X - e2.Metric.Dp(e2.Insets.Right),
Y: e2.Size.Y - e2.Metric.Dp(e2.Insets.Bottom),
},
}
// 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.
func Size(w, h unit.Value) Option {
if w.V <= 0 {
func Size(w, h unit.Dp) Option {
if w <= 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")
}
return func(m unit.Metric, cnf *Config) {
cnf.Mode = Windowed
cnf.Size = image.Point{
X: m.Px(w),
Y: m.Px(h),
X: m.Dp(w),
Y: m.Dp(h),
}
}
}
// MaxSize sets the maximum size of the window.
func MaxSize(w, h unit.Value) Option {
if w.V <= 0 {
func MaxSize(w, h unit.Dp) Option {
if w <= 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")
}
return func(m unit.Metric, cnf *Config) {
cnf.MaxSize = image.Point{
X: m.Px(w),
Y: m.Px(h),
X: m.Dp(w),
Y: m.Dp(h),
}
}
}
// MinSize sets the minimum size of the window.
func MinSize(w, h unit.Value) Option {
if w.V <= 0 {
func MinSize(w, h unit.Dp) Option {
if w <= 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")
}
return func(m unit.Metric, cnf *Config) {
cnf.MinSize = image.Point{
X: m.Px(w),
Y: m.Px(h),
X: m.Dp(w),
Y: m.Dp(h),
}
}
}