~eliasnaur/gio

ad4215a7ebb19bf5063e01bb8f3162fa95c63696 — Elias Naur 2 years ago bde04c6
ui: use ints for unit conversions to pixels

The dp and sp units are approximate and mostly used for layout
dimensions that operate in whole pixels. This change alters the
Config methods to return pixels in ints instead of floats, which
results in smoother use for layout and emphasize the inexactness of
the device independent units.

Clients can still access to raw PxPrDp and PxPrSp factors from
Config.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
M ui/app/os_wayland.go => ui/app/os_wayland.go +2 -2
@@ 237,8 237,8 @@ func createNativeWindow(opts *WindowOptions) (*window, error) {
	C.free(unsafe.Pointer(title))

	_, _, cfg := w.config()
	w.width = int(cfg.Val(opts.Width) + .5)
	w.height = int(cfg.Val(opts.Height) + .5)
	w.width = cfg.Val(opts.Width)
	w.height = cfg.Val(opts.Height)
	if conn.decor != nil {
		// Request server side decorations.
		w.decor = C.zxdg_decoration_manager_v1_get_toplevel_decoration(conn.decor, w.topLvl)

M ui/gesture/gestures.go => ui/gesture/gestures.go +4 -4
@@ 165,9 165,9 @@ func (s *Scroll) Scroll(cfg *ui.Config, q input.Events, axis Axis) int {
				break
			}
			fling := s.estimator.Estimate()
			if slop, d := cfg.Val(touchSlop), fling.Distance; d >= slop || -slop >= d {
				if min, v := cfg.Val(minFlingVelocity), fling.Velocity; v >= min || -min >= v {
					max := cfg.Val(maxFlingVelocity)
			if slop, d := float32(cfg.Val(touchSlop)), fling.Distance; d >= slop || -slop >= d {
				if min, v := float32(cfg.Val(minFlingVelocity)), fling.Velocity; v >= min || -min >= v {
					max := float32(cfg.Val(maxFlingVelocity))
					if v > max {
						v = max
					} else if v < -max {


@@ 201,7 201,7 @@ func (s *Scroll) Scroll(cfg *ui.Config, q input.Events, axis Axis) int {
			dist := s.last - v
			if e.Priority < pointer.Grabbed {
				slop := cfg.Val(touchSlop)
				if dist := float32(dist); dist >= slop || -slop >= dist {
				if dist := dist; dist >= slop || -slop >= dist {
					s.grab = true
				}
			} else {

M ui/measure/measure.go => ui/measure/measure.go +2 -2
@@ 102,7 102,7 @@ func (f *Faces) init() {
}

func (f *textFace) Layout(str string, opts text.LayoutOptions) *text.Layout {
	ppem := fixed.Int26_6(f.faces.Config.Val(f.size)*64 + .5)
	ppem := fixed.Int26_6(f.faces.Config.Val(f.size) * 64)
	lk := layoutKey{
		f:    f.font.Font,
		ppem: ppem,


@@ 120,7 120,7 @@ func (f *textFace) Layout(str string, opts text.LayoutOptions) *text.Layout {
}

func (f *textFace) Path(str text.String) ui.BlockOp {
	ppem := fixed.Int26_6(f.faces.Config.Val(f.size)*64 + .5)
	ppem := fixed.Int26_6(f.faces.Config.Val(f.size) * 64)
	pk := pathKey{
		f:    f.font.Font,
		ppem: ppem,

M ui/text/editor.go => ui/text/editor.go +2 -2
@@ 153,7 153,7 @@ func (e *Editor) Next() (EditorEvent, bool) {
}

func (e *Editor) caretWidth() fixed.Int26_6 {
	oneDp := int(e.Config.Val(ui.Dp(1)) + .5)
	oneDp := e.Config.Val(ui.Dp(1))
	return fixed.Int26_6(oneDp * 64)
}



@@ 167,7 167,7 @@ func (e *Editor) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
			break
		}
	}
	twoDp := int(e.Config.Val(ui.Dp(2)) + 0.5)
	twoDp := e.Config.Val(ui.Dp(2))
	e.padLeft, e.padRight = twoDp, twoDp
	maxWidth := cs.Width.Max
	if e.SingleLine {

M ui/ui.go => ui/ui.go +10 -8
@@ 23,27 23,29 @@ type Config struct {
}

// Dp converts a value in dp units to pixels.
func (c *Config) Dp(dp float32) float32 {
	return c.PxPerDp * dp
func (c *Config) Dp(dp float32) int {
	return c.Val(Dp(dp))
}

// Sp converts a value in sp units to pixels.
func (c *Config) Sp(sp float32) float32 {
	return c.PxPerSp * sp
func (c *Config) Sp(sp float32) int {
	return c.Val(Sp(sp))
}

// Val converts a value to pixels.
func (c *Config) Val(v Value) float32 {
func (c *Config) Val(v Value) int {
	var r float32
	switch v.U {
	case UnitPx:
		return v.V
		r = v.V
	case UnitDp:
		return c.PxPerDp * v.V
		r = c.PxPerDp * v.V
	case UnitSp:
		return c.PxPerSp * v.V
		r = c.PxPerSp * v.V
	default:
		panic("unknown unit")
	}
	return int(math.Round(float64(r)))
}

// LayerOp represents a semantic layer of UI.

M ui/widget/image.go => ui/widget/image.go +6 -4
@@ 25,12 25,14 @@ type Image struct {

func (im Image) Layout(c *ui.Config, ops *ui.Ops, cs layout.Constraints) layout.Dimens {
	size := im.Src.Bounds()
	scale := im.Scale
	if scale == 0 {
	wf, hf := float32(size.Dx()), float32(size.Dy())
	var w, h int
	if im.Scale == 0 {
		const dpPrPx = 160 / 72
		scale = c.Val(ui.Dp(dpPrPx))
		w, h = c.Dp(wf*dpPrPx), c.Dp(hf*dpPrPx)
	} else {
		w, h = int(wf*im.Scale+.5), int(hf*im.Scale+.5)
	}
	w, h := int(float32(size.Dx())*scale+.5), int(float32(size.Dy())*scale+.5)
	d := image.Point{X: cs.Width.Constrain(w), Y: cs.Height.Constrain(h)}
	aspect := float32(w) / float32(h)
	dw, dh := float32(d.X), float32(d.Y)