github.com/jmigpin/editor@v1.6.0/util/uiutil/widget/startpercentlayout.go (about) 1 package widget 2 3 import ( 4 "image" 5 ) 6 7 // Start percent sets the child left X bound to the percent of the size. 8 // This ensures a change in the percentage of a middle child doesn't affect the bounds of the other childs (ex: like causing a small adjustment when resizing). 9 type StartPercentLayout struct { 10 ENode 11 YAxis bool 12 MinimumChildSize int 13 14 minp float64 15 spm map[Node]float64 // start percent map: between 0 and 1 16 } 17 18 func NewStartPercentLayout() *StartPercentLayout { 19 spl := &StartPercentLayout{ 20 spm: make(map[Node]float64), 21 } 22 23 // ensure append uses at least this insertbefore implementation. 24 spl.Wrapper = spl 25 26 return spl 27 } 28 29 //---------- 30 31 func (spl *StartPercentLayout) Measure(hint image.Point) image.Point { 32 return hint 33 } 34 35 func (spl *StartPercentLayout) Layout() { 36 // translate axis 37 xya := XYAxis{spl.YAxis} 38 abounds := xya.Rectangle(&spl.Bounds) 39 40 // update minimum percentage 41 spl.minp = float64(spl.MinimumChildSize) / float64(abounds.Size().X) 42 43 // set sizes 44 dxf := float64(abounds.Dx()) 45 46 spl.IterateWrappers2(func(child Node) { 47 sp := spl.sp(child) 48 ep := spl.ep(child) 49 50 var r image.Rectangle 51 cxs := abounds.Min.X + int(sp*dxf) 52 cxe := abounds.Min.X + int(ep*dxf) 53 54 // set bounds 55 r.Min = image.Point{cxs, abounds.Min.Y} 56 r.Max = image.Point{cxe, abounds.Max.Y} 57 58 // fix last child for rounding errors 59 if child == spl.LastChildWrapper() { 60 r.Max.X = abounds.Max.X 61 } 62 63 // translate axis 64 r2 := xya.Rectangle(&r) 65 66 r3 := r2.Intersect(spl.Bounds) 67 child.Embed().Bounds = r3 68 }) 69 } 70 71 //---------- 72 73 func (spl *StartPercentLayout) InsertBefore(n Node, mark *EmbedNode) { 74 if mark != nil && spl.sp(mark.Wrapper) == 0.0 { 75 // insert after mark 76 spl.ENode.InsertBefore(n, mark.NextSibling()) 77 } else { 78 spl.ENode.InsertBefore(n, mark) 79 } 80 81 ne := n.Embed() 82 if ne.PrevSiblingWrapper() != nil { 83 // share with previous sibling 84 s, e := spl.sp(ne.PrevSiblingWrapper()), spl.ep(n) 85 spl.setsp(n, s+(e-s)/2) 86 } else { 87 // first child 88 spl.setsp(n, 0) 89 } 90 } 91 92 func (spl *StartPercentLayout) Remove(n Node) { 93 delete(spl.spm, n) 94 spl.ENode.Remove(n) 95 } 96 97 //---------- 98 99 // start percent 100 func (spl *StartPercentLayout) sp(n Node) float64 { 101 return spl.spm[n] 102 } 103 104 // end percent 105 func (spl *StartPercentLayout) ep(n Node) float64 { 106 ns := n.Embed().NextSiblingWrapper() 107 if ns != nil { 108 return spl.sp(ns) 109 } 110 return 1.0 111 } 112 113 // start percent of previous 114 func (spl *StartPercentLayout) spPrev(n Node) float64 { 115 ps := n.Embed().PrevSiblingWrapper() 116 if ps != nil { 117 return spl.sp(ps) 118 } 119 return 0.0 120 } 121 122 // set start percent 123 func (spl *StartPercentLayout) setsp(n Node, v float64) { 124 if v < 0 { 125 v = 0 126 } else if v > 1 { 127 v = 1 128 } 129 spl.spm[n] = v 130 131 spl.MarkNeedsLayout() 132 } 133 134 //---------- 135 136 func (spl *StartPercentLayout) size(n Node) float64 { 137 return spl.ep(n) - spl.sp(n) 138 } 139 func (spl *StartPercentLayout) prevSize(n Node) float64 { 140 return spl.sp(n) - spl.spPrev(n) 141 } 142 143 //---------- 144 145 func (spl *StartPercentLayout) Resize(node Node, percent float64) { 146 spl.resize(node, percent) 147 } 148 149 func (spl *StartPercentLayout) resize(node Node, percent float64) { 150 s, e := spl.spPrev(node), spl.ep(node) 151 152 // add margins 153 s2, e2 := s, e 154 if node.Embed() != spl.FirstChild() { 155 s2 += spl.minp 156 } 157 e2 -= spl.minp 158 159 // squash in the middle 160 if node.Embed() != spl.FirstChild() && s2 > e2 { 161 spl.setsp(node, s+(e-s)/2) 162 return 163 } 164 165 // minimum 166 if percent < s2 { 167 spl.setsp(node, s2) 168 return 169 } 170 // maximum 171 if percent > e2 { 172 spl.setsp(node, e2) 173 return 174 } 175 176 spl.setsp(node, percent) 177 } 178 179 //---------- 180 181 func (spl *StartPercentLayout) ResizeWithMove(node Node, percent float64) { 182 minDir := true 183 sp := spl.sp(node) 184 if percent >= sp { 185 minDir = false 186 } 187 _ = spl.resizeWithMove(node, percent, minDir, true) 188 } 189 190 func (spl *StartPercentLayout) resizeWithMove(node Node, percent float64, minDir bool, pusher bool) bool { 191 // preemptively move node 192 moved := false 193 if minDir { 194 for n := node.Embed().PrevSiblingWrapper(); n != nil; n = n.Embed().PrevSiblingWrapper() { 195 s := spl.spPrev(n) 196 e := spl.sp(n) 197 if s < percent && percent < e { 198 // directly use EmbedNode remove/insertbefore 199 spl.ENode.Remove(node) 200 spl.ENode.InsertBefore(node, n.Embed()) 201 moved = true 202 break 203 } 204 } 205 } else { 206 for n := node.Embed().NextSiblingWrapper(); n != nil; n = n.Embed().NextSiblingWrapper() { 207 s := spl.sp(n) 208 e := spl.ep(n) 209 if s < percent && percent < e { 210 // directly use EmbedNode remove/insertbefore 211 spl.ENode.Remove(node) 212 spl.ENode.InsertBefore(node, n.Embed().NextSibling()) 213 moved = true 214 break 215 } 216 } 217 } 218 219 spl.resize(node, percent) 220 221 return moved 222 } 223 224 //---------- 225 226 func (spl *StartPercentLayout) SetPercentWithPush(node Node, percentPos float64) { 227 sp := spl.sp(node) 228 if percentPos > sp { 229 percent := percentPos - sp 230 _ = spl.incStartBy(node, percent) 231 } else { 232 percent := sp - percentPos 233 _ = spl.decStartBy(node, percent) 234 } 235 } 236 237 //---------- 238 239 func (spl *StartPercentLayout) SetSizePercentWithPush(node Node, sizePercent float64) { 240 size := spl.size(node) 241 if size < sizePercent { 242 d := sizePercent - size 243 // push next siblings 244 d -= spl.incStartBy(node.Embed().NextSiblingWrapper(), d) 245 // push prev siblings 246 _ = spl.decStartBy(node, d) 247 } 248 } 249 250 //---------- 251 252 func (spl *StartPercentLayout) incStartBy(node Node, percent float64) float64 { 253 if percent < 0.00001 { 254 return 0.0 255 } 256 if node == nil { 257 return 0.0 258 } 259 size := spl.size(node) 260 if size-percent >= spl.minp { 261 spl.setsp(node, spl.sp(node)+percent) 262 return percent 263 } 264 265 // at this node 266 w := size - spl.minp 267 if w < 0 { 268 w = 0 269 } 270 spl.setsp(node, spl.sp(node)+w) 271 272 // at siblings 273 w2 := spl.incStartBy(node.Embed().NextSiblingWrapper(), percent-w) 274 spl.setsp(node, spl.sp(node)+w2) 275 276 return w + w2 277 } 278 279 func (spl *StartPercentLayout) decStartBy(node Node, percent float64) float64 { 280 if percent < 0.00001 { 281 return 0.0 282 } 283 if node == nil { 284 return 0.0 285 } 286 287 minp := spl.minp 288 if node.Embed() == spl.FirstChild() { 289 minp = 0.0 290 } 291 292 size := spl.prevSize(node) 293 if size-percent >= minp { 294 spl.setsp(node, spl.sp(node)-percent) 295 return percent 296 } 297 298 // at this node 299 w := size - minp 300 if w < 0 { 301 w = 0 302 } 303 spl.setsp(node, spl.sp(node)-w) 304 305 // at siblings 306 w2 := spl.decStartBy(node.Embed().PrevSiblingWrapper(), percent-w) 307 spl.setsp(node, spl.sp(node)-w2) 308 309 return w + w2 310 } 311 312 //---------- 313 314 func (spl *StartPercentLayout) MaximizeNode(node Node) { 315 n := spl.ChildsLen() 316 sp := 0.0 317 i := 0 318 spl.IterateWrappers2(func(c Node) { 319 spl.setsp(c, sp) 320 if c == node { 321 sp = 1.0 - spl.minp*float64(n-(i+1)) 322 } else { 323 sp += spl.minp 324 } 325 i++ 326 }) 327 } 328 329 //---------- 330 331 // Used for encoding/decoding only. (Ex: sessions) 332 func (spl *StartPercentLayout) SetRawStartPercent(child Node, v float64) { 333 // detect oddly placed rows (two rows with same value due to manual edit of the sessions file) 334 for n, k := range spl.spm { 335 if n == child { 336 continue 337 } 338 if k == v { 339 // return and don't set the value, will keep the automatically assigned percent value uppon child creation. Fixes allowing rows to be hidden with the same percent value on restoring a session. 340 return 341 } 342 } 343 344 spl.setsp(child, v) 345 } 346 347 // Used for encoding/decoding only. (Ex: sessions) 348 func (spl *StartPercentLayout) RawStartPercent(child Node) float64 { 349 return spl.sp(child) 350 }