~eliasnaur/gio

gio/unit/unit.go -rw-r--r-- 3.0 KiB
ef7b3e75Jack Mordaunt widget: delete whole words with key modifier 4 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// SPDX-License-Identifier: Unlicense OR MIT

/*

Package unit implements device independent units and values.

A Value is a value with a Unit attached.

Device independent pixel, or dp, is the unit for sizes independent of
the underlying display device.

Scaled pixels, or sp, is the unit for text sizes. An sp is like dp with
text scaling applied.

Finally, pixels, or px, is the unit for display dependent pixels. Their
size vary between platforms and displays.

To maintain a constant visual size across platforms and displays, always
use dps or sps to define user interfaces. Only use pixels for derived
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 {
	// PxPerDp is the device-dependent pixels per dp.
	PxPerDp float32
	// PxPerSp is the device-dependent pixels per sp.
	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
	// have the same apparent size across platforms and
	// display resolutions.
	UnitDp
	// UnitSp is like UnitDp but for font sizes.
	UnitSp
)

// 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")
	}
}

// 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
	}
	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)))
}