github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/layout/layout.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package layout 4 5 import ( 6 "image" 7 8 "github.com/gop9/olt/gio/op" 9 "github.com/gop9/olt/gio/unit" 10 ) 11 12 // Constraints represent a set of acceptable ranges for 13 // a widget's width and height. 14 type Constraints struct { 15 Width Constraint 16 Height Constraint 17 } 18 19 // Constraint is a range of acceptable sizes in a single 20 // dimension. 21 type Constraint struct { 22 Min, Max int 23 } 24 25 // Dimensions are the resolved size and baseline for a widget. 26 type Dimensions struct { 27 Size image.Point 28 Baseline int 29 } 30 31 // Axis is the Horizontal or Vertical direction. 32 type Axis uint8 33 34 // Alignment is the mutual alignment of a list of widgets. 35 type Alignment uint8 36 37 // Direction is the alignment of widgets relative to a containing 38 // space. 39 type Direction uint8 40 41 // Widget is a function scope for drawing, processing events and 42 // computing dimensions for a user interface element. 43 type Widget func() 44 45 const ( 46 Start Alignment = iota 47 End 48 Middle 49 Baseline 50 ) 51 52 const ( 53 NW Direction = iota 54 N 55 NE 56 E 57 SE 58 S 59 SW 60 W 61 Center 62 ) 63 64 const ( 65 Horizontal Axis = iota 66 Vertical 67 ) 68 69 // Constrain a value to the range [Min; Max]. 70 func (c Constraint) Constrain(v int) int { 71 if v < c.Min { 72 return c.Min 73 } else if v > c.Max { 74 return c.Max 75 } 76 return v 77 } 78 79 // Constrain a size to the Width and Height ranges. 80 func (c Constraints) Constrain(size image.Point) image.Point { 81 return image.Point{X: c.Width.Constrain(size.X), Y: c.Height.Constrain(size.Y)} 82 } 83 84 // RigidConstraints returns the constraints that can only be 85 // satisfied by the given dimensions. 86 func RigidConstraints(size image.Point) Constraints { 87 return Constraints{ 88 Width: Constraint{Min: size.X, Max: size.X}, 89 Height: Constraint{Min: size.Y, Max: size.Y}, 90 } 91 } 92 93 // Inset adds space around a widget. 94 type Inset struct { 95 Top, Right, Bottom, Left unit.Value 96 } 97 98 // Align aligns a widget in the available space. 99 type Align Direction 100 101 // Layout a widget. 102 func (in Inset) Layout(gtx *Context, w Widget) { 103 top := gtx.Px(in.Top) 104 right := gtx.Px(in.Right) 105 bottom := gtx.Px(in.Bottom) 106 left := gtx.Px(in.Left) 107 mcs := gtx.Constraints 108 mcs.Width.Max -= left + right 109 if mcs.Width.Max < 0 { 110 left = 0 111 right = 0 112 mcs.Width.Max = 0 113 } 114 if mcs.Width.Min > mcs.Width.Max { 115 mcs.Width.Min = mcs.Width.Max 116 } 117 mcs.Height.Max -= top + bottom 118 if mcs.Height.Max < 0 { 119 bottom = 0 120 top = 0 121 mcs.Height.Max = 0 122 } 123 if mcs.Height.Min > mcs.Height.Max { 124 mcs.Height.Min = mcs.Height.Max 125 } 126 var stack op.StackOp 127 stack.Push(gtx.Ops) 128 op.TransformOp{}.Offset(toPointF(image.Point{X: left, Y: top})).Add(gtx.Ops) 129 dims := ctxLayout(gtx, mcs, w) 130 stack.Pop() 131 gtx.Dimensions = Dimensions{ 132 Size: dims.Size.Add(image.Point{X: right + left, Y: top + bottom}), 133 Baseline: dims.Baseline + bottom, 134 } 135 } 136 137 // UniformInset returns an Inset with a single inset applied to all 138 // edges. 139 func UniformInset(v unit.Value) Inset { 140 return Inset{Top: v, Right: v, Bottom: v, Left: v} 141 } 142 143 // Layout a widget. 144 func (a Align) Layout(gtx *Context, w Widget) { 145 var macro op.MacroOp 146 macro.Record(gtx.Ops) 147 cs := gtx.Constraints 148 mcs := cs 149 mcs.Width.Min = 0 150 mcs.Height.Min = 0 151 dims := ctxLayout(gtx, mcs, w) 152 macro.Stop() 153 sz := dims.Size 154 if sz.X < cs.Width.Min { 155 sz.X = cs.Width.Min 156 } 157 if sz.Y < cs.Height.Min { 158 sz.Y = cs.Height.Min 159 } 160 var p image.Point 161 switch Direction(a) { 162 case N, S, Center: 163 p.X = (sz.X - dims.Size.X) / 2 164 case NE, SE, E: 165 p.X = sz.X - dims.Size.X 166 } 167 switch Direction(a) { 168 case W, Center, E: 169 p.Y = (sz.Y - dims.Size.Y) / 2 170 case SW, S, SE: 171 p.Y = sz.Y - dims.Size.Y 172 } 173 var stack op.StackOp 174 stack.Push(gtx.Ops) 175 op.TransformOp{}.Offset(toPointF(p)).Add(gtx.Ops) 176 macro.Add() 177 stack.Pop() 178 gtx.Dimensions = Dimensions{ 179 Size: sz, 180 Baseline: dims.Baseline + sz.Y - dims.Size.Y - p.Y, 181 } 182 } 183 184 func (a Alignment) String() string { 185 switch a { 186 case Start: 187 return "Start" 188 case End: 189 return "End" 190 case Middle: 191 return "Middle" 192 case Baseline: 193 return "Baseline" 194 default: 195 panic("unreachable") 196 } 197 } 198 199 func (a Axis) String() string { 200 switch a { 201 case Horizontal: 202 return "Horizontal" 203 case Vertical: 204 return "Vertical" 205 default: 206 panic("unreachable") 207 } 208 } 209 210 func (d Direction) String() string { 211 switch d { 212 case NW: 213 return "NW" 214 case N: 215 return "N" 216 case NE: 217 return "NE" 218 case E: 219 return "E" 220 case SE: 221 return "SE" 222 case S: 223 return "S" 224 case SW: 225 return "SW" 226 case W: 227 return "W" 228 case Center: 229 return "Center" 230 default: 231 panic("unreachable") 232 } 233 }