~eliasnaur/gio

596e32161041eb3b4b064942a0be5cb728a3793a — Elias Naur 5 months ago 1603a6f
all: make unit.Converter concrete and rename to Metric

An interface for scaling dp and sp is overkill, at least for all
current uses. Make it a concrete struct type, and rename it to the
shorter and more precise Metric.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
M app/internal/window/os_android.go => app/internal/window/os_android.go +3 -3
@@ 396,9 396,9 @@ func (w *window) draw(sync bool) {
				Y: int(height),
			},
			Insets: w.insets,
			Config: &config{
				pxPerDp: ppdp,
				pxPerSp: w.fontScale * ppdp,
			Metric: unit.Metric{
				PxPerDp: ppdp,
				PxPerSp: w.fontScale * ppdp,
			},
		},
		Sync: sync,

M app/internal/window/os_ios.go => app/internal/window/os_ios.go +3 -3
@@ 119,9 119,9 @@ func (w *window) draw(sync bool) {
				Bottom: unit.Px(float32(params.bottom)),
				Left:   unit.Px(float32(params.left)),
			},
			Config: &config{
				pxPerDp: float32(params.dpi) * inchPrDp,
				pxPerSp: float32(params.sdpi) * inchPrDp,
			Metric: unit.Metric{
				PxPerDp: float32(params.dpi) * inchPrDp,
				PxPerSp: float32(params.sdpi) * inchPrDp,
			},
		},
		Sync: sync,

M app/internal/window/os_js.go => app/internal/window/os_js.go +7 -6
@@ 15,6 15,7 @@ import (
	"gioui.org/io/key"
	"gioui.org/io/pointer"
	"gioui.org/io/system"
	"gioui.org/unit"
)

type window struct {


@@ 407,7 408,7 @@ func (w *window) ShowTextInput(show bool) {

func (w *window) draw(sync bool) {
	width, height, scale, cfg := w.config()
	if cfg == (config{}) || width == 0 || height == 0 {
	if cfg == (unit.Metric{}) || width == 0 || height == 0 {
		return
	}
	w.mu.Lock()


@@ 420,13 421,13 @@ func (w *window) draw(sync bool) {
				X: width,
				Y: height,
			},
			Config: &cfg,
			Metric: cfg,
		},
		Sync: sync,
	})
}

func (w *window) config() (int, int, float32, config) {
func (w *window) config() (int, int, float32, unit.Metric) {
	rect := w.cnv.Call("getBoundingClientRect")
	width, height := rect.Get("width").Float(), rect.Get("height").Float()
	scale := w.window.Get("devicePixelRatio").Float()


@@ 438,9 439,9 @@ func (w *window) config() (int, int, float32, config) {
		w.cnv.Set("width", iw)
		w.cnv.Set("height", ih)
	}
	return iw, ih, float32(scale), config{
		pxPerDp: float32(scale),
		pxPerSp: float32(scale),
	return iw, ih, float32(scale), unit.Metric{
		PxPerDp: float32(scale),
		PxPerSp: float32(scale),
	}
}


M app/internal/window/os_macos.go => app/internal/window/os_macos.go +6 -5
@@ 17,6 17,7 @@ import (
	"gioui.org/io/key"
	"gioui.org/io/pointer"
	"gioui.org/io/system"
	"gioui.org/unit"

	_ "gioui.org/app/internal/cocoainit"
)


@@ 236,16 237,16 @@ func (w *window) draw() {
				X: width,
				Y: height,
			},
			Config: &cfg,
			Metric: cfg,
		},
		Sync: true,
	})
}

func configFor(scale float32) config {
	return config{
		pxPerDp: scale,
		pxPerSp: scale,
func configFor(scale float32) unit.Metric {
	return unit.Metric{
		PxPerDp: scale,
		PxPerSp: scale,
	}
}


M app/internal/window/os_wayland.go => app/internal/window/os_wayland.go +8 -7
@@ 25,6 25,7 @@ import (
	"gioui.org/io/key"
	"gioui.org/io/pointer"
	"gioui.org/io/system"
	"gioui.org/unit"
	syscall "golang.org/x/sys/unix"
)



@@ 873,7 874,7 @@ func (w *window) flushFling() {
	w.fling.yExtrapolation = fling.Extrapolation{}
	vel := float32(math.Sqrt(float64(estx.Velocity*estx.Velocity + esty.Velocity*esty.Velocity)))
	_, _, c := w.config()
	if !w.fling.anim.Start(&c, time.Now(), vel) {
	if !w.fling.anim.Start(c, time.Now(), vel) {
		return
	}
	invDist := 1 / vel


@@ 1359,11 1360,11 @@ func (w *window) updateOutputs() {
	}
}

func (w *window) config() (int, int, config) {
func (w *window) config() (int, int, unit.Metric) {
	width, height := w.width*w.scale, w.height*w.scale
	return width, height, config{
		pxPerDp: w.ppdp * float32(w.scale),
		pxPerSp: w.ppsp * float32(w.scale),
	return width, height, unit.Metric{
		PxPerDp: w.ppdp * float32(w.scale),
		PxPerSp: w.ppsp * float32(w.scale),
	}
}



@@ 1377,7 1378,7 @@ func (w *window) draw(sync bool) {
		return
	}
	width, height, cfg := w.config()
	if cfg == (config{}) {
	if cfg == (unit.Metric{}) {
		return
	}
	if anim && w.lastFrameCallback == nil {


@@ 1392,7 1393,7 @@ func (w *window) draw(sync bool) {
				X: width,
				Y: height,
			},
			Config: &cfg,
			Metric: cfg,
		},
		Sync: sync,
	})

M app/internal/window/os_windows.go => app/internal/window/os_windows.go +6 -5
@@ 19,6 19,7 @@ import (
	syscall "golang.org/x/sys/windows"

	"gioui.org/app/internal/windows"
	"gioui.org/unit"

	"gioui.org/f32"
	"gioui.org/io/key"


@@ 377,7 378,7 @@ func (w *window) draw(sync bool) {
				X: w.width,
				Y: w.height,
			},
			Config: &cfg,
			Metric: cfg,
		},
		Sync: sync,
	})


@@ 586,12 587,12 @@ func convertKeyCode(code uintptr) (string, bool) {
	return r, true
}

func configForDC() config {
func configForDC() unit.Metric {
	dpi := windows.GetSystemDPI()
	const inchPrDp = 1.0 / 96.0
	ppdp := float32(dpi) * inchPrDp
	return config{
		pxPerDp: ppdp,
		pxPerSp: ppdp,
	return unit.Metric{
		PxPerDp: ppdp,
		PxPerSp: ppdp,
	}
}

M app/internal/window/os_x11.go => app/internal/window/os_x11.go +4 -3
@@ 37,6 37,7 @@ import (
	"gioui.org/io/key"
	"gioui.org/io/pointer"
	"gioui.org/io/system"
	"gioui.org/unit"

	"gioui.org/app/internal/xkb"
	syscall "golang.org/x/sys/unix"


@@ 64,7 65,7 @@ type x11Window struct {
		atom C.Atom
	}
	stage  system.Stage
	cfg    config
	cfg    unit.Metric
	width  int
	height int
	notify struct {


@@ 193,7 194,7 @@ loop:
						X: w.width,
						Y: w.height,
					},
					Config: &w.cfg,
					Metric: w.cfg,
				},
				Sync: syn,
			})


@@ 474,7 475,7 @@ func newX11Window(gioWin Callbacks, opts *Options) error {
	}

	ppsp := x11DetectUIScale(dpy)
	cfg := config{pxPerDp: ppsp, pxPerSp: ppsp}
	cfg := unit.Metric{PxPerDp: ppsp, PxPerSp: ppsp}
	swa := C.XSetWindowAttributes{
		event_mask: C.ExposureMask | C.FocusChangeMask | // update
			C.KeyPressMask | C.KeyReleaseMask | // keyboard

M app/internal/window/window.go => app/internal/window/window.go +0 -24
@@ 6,7 6,6 @@ package window

import (
	"errors"
	"math"

	"gioui.org/gpu/backend"
	"gioui.org/io/event"


@@ 71,29 70,6 @@ type windowAndOptions struct {
	opts   *Options
}

// config implements the system.Config interface.
type config struct {
	// Device pixels per dp.
	pxPerDp float32
	// Device pixels per sp.
	pxPerSp float32
}

func (c *config) Px(v unit.Value) int {
	var r float32
	switch v.U {
	case unit.UnitPx:
		r = v.V
	case unit.UnitDp:
		r = c.pxPerDp * v.V
	case unit.UnitSp:
		r = c.pxPerSp * v.V
	default:
		panic("unknown unit")
	}
	return int(math.Round(float64(r)))
}

func newWindowRendezvous() *windowRendezvous {
	wr := &windowRendezvous{
		in:   make(chan windowAndOptions),

M gesture/gesture.go => gesture/gesture.go +1 -1
@@ 204,7 204,7 @@ func (s *Scroll) Stop() {

// Scroll detects the scrolling distance from the available events and
// ongoing fling gestures.
func (s *Scroll) Scroll(cfg unit.Converter, q event.Queue, t time.Time, axis Axis) int {
func (s *Scroll) Scroll(cfg unit.Metric, q event.Queue, t time.Time, axis Axis) int {
	if s.axis != axis {
		s.axis = axis
		return 0

M internal/fling/animation.go => internal/fling/animation.go +1 -1
@@ 31,7 31,7 @@ const (

// Start a fling given a starting velocity. Returns whether a
// fling was started.
func (f *Animation) Start(c unit.Converter, now time.Time, velocity float32) bool {
func (f *Animation) Start(c unit.Metric, now time.Time, velocity float32) bool {
	min := float32(c.Px(minFlingVelocity))
	v := velocity
	if -min <= v && v <= min {

M io/system/system.go => io/system/system.go +2 -7
@@ 17,10 17,11 @@ import (
// operations that describes what to display and how to handle
// input.
type FrameEvent struct {
	Config Config
	// Now is the current animation. Use Now instead of time.Now to
	// synchronize animation and to avoid the time.Now call overhead.
	Now time.Time
	// Metric converts device independent dp and sp to device pixels.
	Metric unit.Metric
	// Size is the dimensions of the window.
	Size image.Point
	// Insets is the insets to apply.


@@ 51,12 52,6 @@ type FrameEvent struct {
	Queue event.Queue
}

// Config defines the essential properties of
// the environment.
type Config interface {
	unit.Converter
}

// DestroyEvent is the last event sent through
// a window event channel.
type DestroyEvent struct {

M layout/context.go => layout/context.go +4 -9
@@ 3,7 3,6 @@
package layout

import (
	"math"
	"time"

	"gioui.org/io/event"


@@ 20,7 19,7 @@ type Context struct {
	// layout.
	Constraints Constraints

	Config system.Config
	Metric unit.Metric
	// By convention, a nil Queue is a signal to widgets to draw themselves
	// in a disabled state.
	Queue event.Queue


@@ 47,18 46,14 @@ func NewContext(ops *op.Ops, e system.FrameEvent) Context {
		Ops:         ops,
		Now:         e.Now,
		Queue:       e.Queue,
		Config:      e.Config,
		Metric:      e.Metric,
		Constraints: Exact(e.Size),
	}
}

// Px maps the value to pixels. If no configuration is set,
// Px returns the rounded value of v.
// Px maps the value to pixels.
func (c Context) Px(v unit.Value) int {
	if c.Config == nil {
		return int(math.Round(float64(v.V)))
	}
	return c.Config.Px(v)
	return c.Metric.Px(v)
}

// Events returns the events available for the key. If no

M layout/list.go => layout/list.go +1 -1
@@ 121,7 121,7 @@ func (l *List) Dragging() bool {
}

func (l *List) update() {
	d := l.scroll.Scroll(l.ctx, l.ctx, l.ctx.Now, gesture.Axis(l.Axis))
	d := l.scroll.Scroll(l.ctx.Metric, l.ctx, l.ctx.Now, gesture.Axis(l.Axis))
	l.scrollDelta = d
	l.Position.Offset += d
}

M unit/unit.go => unit/unit.go +37 -7
@@ 22,7 22,10 @@ values.
*/
package unit

import "fmt"
import (
	"fmt"
	"math"
)

// Value is a value with a unit.
type Value struct {


@@ 33,9 36,13 @@ type Value struct {
// Unit represents a unit for a Value.
type Unit uint8

// Converter converts Values to pixels.
type Converter interface {
	Px(v Value) int
// 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 {
	// PxPerDp is the device-dependent pixels per dp.
	PxPerDp float32
	// PxPerSp is the device-dependent pixels per sp.
	PxPerSp float32
}

const (


@@ 90,7 97,7 @@ func (u Unit) String() string {
}

// Add a list of Values.
func Add(c Converter, values ...Value) Value {
func Add(c Metric, values ...Value) Value {
	var sum Value
	for _, v := range values {
		sum, v = compatible(c, sum, v)


@@ 100,7 107,7 @@ func Add(c Converter, values ...Value) Value {
}

// Max returns the maximum of a list of Values.
func Max(c Converter, values ...Value) Value {
func Max(c Metric, values ...Value) Value {
	var max Value
	for _, v := range values {
		max, v = compatible(c, max, v)


@@ 111,7 118,30 @@ func Max(c Converter, values ...Value) Value {
	return max
}

func compatible(c Converter, v1, v2 Value) (Value, Value) {
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
	}

M widget/editor.go => widget/editor.go +3 -3
@@ 129,7 129,7 @@ func (e *Editor) processPointer(gtx layout.Context) {
		axis = gesture.Vertical
		smin, smax = sbounds.Min.Y, sbounds.Max.Y
	}
	sdist := e.scroller.Scroll(gtx, gtx, gtx.Now, axis)
	sdist := e.scroller.Scroll(gtx.Metric, gtx, gtx.Now, axis)
	var soff int
	if e.SingleLine {
		e.scrollRel(sdist, 0)


@@ 143,7 143,7 @@ func (e *Editor) processPointer(gtx layout.Context) {
		case evt.Type == gesture.TypePress && evt.Source == pointer.Mouse,
			evt.Type == gesture.TypeClick && evt.Source == pointer.Touch:
			e.blinkStart = gtx.Now
			e.moveCoord(gtx, image.Point{
			e.moveCoord(gtx.Metric, image.Point{
				X: int(math.Round(float64(evt.Position.X))),
				Y: int(math.Round(float64(evt.Position.Y))),
			})


@@ 426,7 426,7 @@ func (e *Editor) scrollAbs(x, y int) {
	}
}

func (e *Editor) moveCoord(c unit.Converter, pos image.Point) {
func (e *Editor) moveCoord(c unit.Metric, pos image.Point) {
	var (
		prevDesc fixed.Int26_6
		carLine  int