github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/unit/unit.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 /* 4 5 Package unit implements device independent units and values. 6 7 A Value is a value with a Unit attached. 8 9 Device independent pixel, or dp, is the unit for sizes independent of 10 the underlying display device. 11 12 Scaled pixels, or sp, is the unit for text sizes. An sp is like dp with 13 text scaling applied. 14 15 Finally, pixels, or px, is the unit for display dependent pixels. Their 16 size vary between platforms and displays. 17 18 To maintain a constant visual size across platforms and displays, always 19 use dps or sps to define user interfaces. Only use pixels for derived 20 values. 21 22 */ 23 package unit 24 25 import ( 26 "fmt" 27 "math" 28 ) 29 30 // Value is a value with a unit. 31 type Value struct { 32 V float32 33 U Unit 34 } 35 36 // Unit represents a unit for a Value. 37 type Unit uint8 38 39 // Metric converts Values to device-dependent pixels, px. The zero 40 // value represents a 1-to-1 scale from dp, sp to pixels. 41 type Metric struct { 42 // PxPerDp is the device-dependent pixels per dp. 43 PxPerDp float32 44 // PxPerSp is the device-dependent pixels per sp. 45 PxPerSp float32 46 } 47 48 const ( 49 // UnitPx represent device pixels in the resolution of 50 // the underlying display. 51 UnitPx Unit = iota 52 // UnitDp represents device independent pixels. 1 dp will 53 // have the same apparent size across platforms and 54 // display resolutions. 55 UnitDp 56 // UnitSp is like UnitDp but for font sizes. 57 UnitSp 58 ) 59 60 // Px returns the Value for v device pixels. 61 func Px(v float32) Value { 62 return Value{V: v, U: UnitPx} 63 } 64 65 // Dp returns the Value for v device independent 66 // pixels. 67 func Dp(v float32) Value { 68 return Value{V: v, U: UnitDp} 69 } 70 71 // Sp returns the Value for v scaled dps. 72 func Sp(v float32) Value { 73 return Value{V: v, U: UnitSp} 74 } 75 76 // Scale returns the value scaled by s. 77 func (v Value) Scale(s float32) Value { 78 v.V *= s 79 return v 80 } 81 82 func (v Value) String() string { 83 return fmt.Sprintf("%g%s", v.V, v.U) 84 } 85 86 func (u Unit) String() string { 87 switch u { 88 case UnitPx: 89 return "px" 90 case UnitDp: 91 return "dp" 92 case UnitSp: 93 return "sp" 94 default: 95 panic("unknown unit") 96 } 97 } 98 99 // Add a list of Values. 100 func Add(c Metric, values ...Value) Value { 101 var sum Value 102 for _, v := range values { 103 sum, v = compatible(c, sum, v) 104 sum.V += v.V 105 } 106 return sum 107 } 108 109 // Max returns the maximum of a list of Values. 110 func Max(c Metric, values ...Value) Value { 111 var max Value 112 for _, v := range values { 113 max, v = compatible(c, max, v) 114 if v.V > max.V { 115 max.V = v.V 116 } 117 } 118 return max 119 } 120 121 func (c Metric) Px(v Value) int { 122 var r float32 123 switch v.U { 124 case UnitPx: 125 r = v.V 126 case UnitDp: 127 s := c.PxPerDp 128 if s == 0 { 129 s = 1 130 } 131 r = s * v.V 132 case UnitSp: 133 s := c.PxPerSp 134 if s == 0 { 135 s = 1 136 } 137 r = s * v.V 138 default: 139 panic("unknown unit") 140 } 141 return int(math.Round(float64(r))) 142 } 143 144 func compatible(c Metric, v1, v2 Value) (Value, Value) { 145 if v1.U == v2.U { 146 return v1, v2 147 } 148 if v1.V == 0 { 149 v1.U = v2.U 150 return v1, v2 151 } 152 if v2.V == 0 { 153 v2.U = v1.U 154 return v1, v2 155 } 156 return Px(float32(c.Px(v1))), Px(float32(c.Px(v2))) 157 }