Flexbox
Flexbox is a high level positioning plugin that allows for dynamic positioning of elements based apon container sizing. To learn more about flexbox refer to this MDN article, in this document we will go into the impleamentation of flexbox rather than the usage of it.
"display": "flex", "justify-content": "*", "align-content": "*", "align-items": "*", "flex-wrap": "*", "flex-direction": "*",
# Flex Properties
justify-content | align-content | align-items | flex-wrap | flex-direction |
---|---|---|---|---|
normal | normal | normal | nowrap | row |
center | center | center | wrap | column |
flex-start | flex-start | flex-start | row-reverse | |
flex-end | flex-end | flex-end | column-reverse | |
space-between | space-between | stretch | ||
space-around | space-around | baseline | ||
space-evenly | stretch |
# order?(go)
asd
1package flex
2
3import (
4 "gui/cstyle"
5 "gui/cstyle/plugins/inline"
6 "gui/element"
7 "gui/utils"
8 "sort"
9 "strings"
10)
11
12// !ISSUES: Text disapearing (i think its the inline plugin)
13// + widths
14// + wrpa wraps too soon
15
16func Init() cstyle.Plugin {
17 return cstyle.Plugin{
18 Selector: func(n *element.Node) bool {
19 styles := map[string]string{
20 "display": "flex",
21 }
22 matches := true
23 for name, value := range styles {
24 if n.Style[name] != value && !(value == "*") && n.Style[name] != "" {
25 matches = false
26 }
27 }
28 return matches
29 },
30 Level: 3,
31 Handler: func(n *element.Node, state *map[string]element.State) {
32 s := *state
33 self := s[n.Properties.Id]
34
35 verbs := strings.Split(n.Style["flex-direction"], "-")
36 flexDirection := verbs[0]
37 if flexDirection == "" {
38 flexDirection = "row"
39 }
40 flexReversed := false
41 if len(verbs) > 1 {
42 flexReversed = true
43 }
44
45 var flexWrapped bool
46 if n.Style["flex-wrap"] == "wrap" {
47 flexWrapped = true
48 } else {
49 flexWrapped = false
50 }
51
52 hAlign := n.Style["align-content"]
53 if hAlign == "" {
54 hAlign = "normal"
55 }
56 vAlign := n.Style["align-items"]
57 if vAlign == "" {
58 vAlign = "normal"
59 }
60 justifyItems := n.Style["justify-items"]
61 if justifyItems == "" {
62 justifyItems = "normal"
63 }
64
65 justifyContent := n.Style["justify-content"]
66 if justifyContent == "" {
67 justifyContent = "normal"
68 }
69 // fmt.Println(flexDirection, flexReversed, flexWrapped, hAlign, vAlign, justifyItems, justifyContent)
70
71 if flexDirection == "row" {
72
73 rows := [][]int{}
74 maxH := float32(0)
75
76 // Get inital sizing
77 textTotal := 0
78 textCounts := []int{}
79 widths := []float32{}
80 innerSizes := [][]float32{}
81 minWidths := []float32{}
82 maxWidths := []float32{}
83 for _, v := range n.Children {
84 count := countText(v)
85 textTotal += count
86 textCounts = append(textCounts, count)
87
88 minw := getMinWidth(&v, state)
89 minWidths = append(minWidths, minw)
90
91 maxw := getMaxWidth(&v, state)
92 maxWidths = append(maxWidths, maxw)
93
94 w, h := getInnerSize(&v, state)
95 innerSizes = append(innerSizes, []float32{w, h})
96 }
97 selfWidth := (self.Width - self.Padding.Left) - self.Padding.Right
98 // if the elements are less than the size of the parent, don't change widths. Just set mins
99 if !flexWrapped {
100 if add2d(innerSizes, 0) < selfWidth {
101 for i, v := range n.Children {
102 vState := s[v.Properties.Id]
103
104 w := innerSizes[i][0]
105 w -= vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
106 widths = append(widths, w)
107 }
108 } else {
109 // Modifiy the widths so they aren't under the mins
110 for i, v := range n.Children {
111 vState := s[v.Properties.Id]
112
113 w := ((selfWidth / float32(textTotal)) * float32(textCounts[i]))
114 w -= vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
115
116 if w < minWidths[i] {
117 selfWidth -= minWidths[i] + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
118 textTotal -= textCounts[i]
119 textCounts[i] = 0
120 }
121
122 }
123 for i, v := range n.Children {
124 vState := s[v.Properties.Id]
125
126 w := ((selfWidth / float32(textTotal)) * float32(textCounts[i]))
127 w -= vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
128 // (w!=w) is of NaN
129 if w < minWidths[i] || (w != w) {
130 w = minWidths[i]
131 }
132 widths = append(widths, w)
133 }
134 }
135 // Apply the new widths
136 fState := s[n.Children[0].Properties.Id]
137 for i, v := range n.Children {
138 vState := s[v.Properties.Id]
139
140 vState.Width = widths[i]
141 xStore := vState.X
142 if i > 0 {
143 sState := s[n.Children[i-1].Properties.Id]
144 vState.X = sState.X + sState.Width + sState.Margin.Right + vState.Margin.Left + sState.Border.Width + vState.Border.Width
145 propagateOffsets(&v, xStore, vState.Y, vState.X, fState.Y, state)
146 }
147
148 vState.Y = fState.Y
149
150 (*state)[v.Properties.Id] = vState
151 deInline(&v, state)
152 applyInline(&v, state)
153 applyBlock(&v, state)
154 _, h := getInnerSize(&v, state)
155 h = utils.Max(h, vState.Height)
156 maxH = utils.Max(maxH, h)
157 }
158 // When not wrapping everything will be on the same row
159 rows = append(rows, []int{0, len(n.Children), int(maxH)})
160 } else {
161 // Flex Wrapped
162 sum := innerSizes[0][0]
163 for i := 0; i < len(n.Children); i++ {
164 v := n.Children[i]
165 vState := s[v.Properties.Id]
166
167 // if the next plus current will break then
168 w := innerSizes[i][0]
169 if i > 0 {
170 sib := s[n.Children[i-1].Properties.Id]
171 if maxWidths[i] > selfWidth {
172 w = selfWidth - vState.Margin.Left - vState.Margin.Right - (vState.Border.Width * 2)
173 }
174 if w+sum > selfWidth {
175 sum = w + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
176 } else {
177 propagateOffsets(&v, vState.X, vState.Y, vState.X, sib.Y, state)
178 vState.Y = sib.Y
179 (*state)[v.Properties.Id] = vState
180 sum += w + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
181 }
182 }
183
184 widths = append(widths, w)
185 }
186
187 // Move the elements into the correct position
188 start := 0
189 var prevOffset float32
190 for i := 0; i < len(n.Children); i++ {
191 v := n.Children[i]
192 vState := s[v.Properties.Id]
193
194 vState.Width = widths[i]
195 xStore := vState.X
196 yStore := vState.Y
197
198 if i > 0 {
199 sib := s[n.Children[i-1].Properties.Id]
200 if vState.Y+prevOffset == sib.Y {
201 yStore += prevOffset
202
203 if vState.Height < sib.Height {
204 vState.Height = sib.Height
205 }
206 // Shift right if on a row with sibling
207 xStore = sib.X + sib.Width + sib.Margin.Right + sib.Border.Width + vState.Margin.Left + vState.Border.Width
208 } else {
209 // Shift under sibling
210 yStore = sib.Y + sib.Height + sib.Margin.Top + sib.Margin.Bottom + sib.Border.Width*2
211 prevOffset = yStore - vState.Y
212 rows = append(rows, []int{start, i, int(maxH)})
213 start = i
214 maxH = 0
215 }
216 propagateOffsets(&v, vState.X, vState.Y, xStore, yStore, state)
217 }
218 vState.X = xStore
219 vState.Y = yStore
220
221 (*state)[v.Properties.Id] = vState
222 deInline(&v, state)
223 applyInline(&v, state)
224 applyBlock(&v, state)
225 _, h := getInnerSize(&v, state)
226 h = utils.Max(h, vState.Height)
227 maxH = utils.Max(maxH, h)
228 vState.Height = h
229 (*state)[v.Properties.Id] = vState
230 }
231 if start < len(n.Children) {
232 rows = append(rows, []int{start, len(n.Children), int(maxH)})
233 }
234 }
235
236 for _, v := range rows {
237 for i := v[0]; i < v[1]; i++ {
238 vState := s[n.Children[i].Properties.Id]
239 vState.Height = float32(v[2])
240 (*state)[n.Children[i].Properties.Id] = vState
241 }
242 }
243 // Reverse elements
244 if flexReversed {
245 rowReverse(rows, n, state)
246 }
247
248 if justifyContent != "" && justifyContent != "normal" {
249 justifyRow(rows, n, state, justifyContent, flexReversed)
250 }
251 }
252
253 // Column doesn't really need a lot done bc it is basically block styling rn
254 if flexDirection == "column" && flexReversed {
255 colReverse(n, state)
256 }
257 if n.Style["height"] == "" {
258 _, h := getInnerSize(n, state)
259 self.Height = h
260 }
261 (*state)[n.Properties.Id] = self
262 },
263 }
264}
265
266func applyBlock(n *element.Node, state *map[string]element.State) {
267 accum := float32(0)
268 inlineOffset := float32(0)
269 s := *state
270 lastHeight := float32(0)
271 baseY := s[n.Children[0].Properties.Id].Y
272 for i := 0; i < len(n.Children); i++ {
273 v := &n.Children[i]
274 vState := s[v.Properties.Id]
275
276 if v.Style["display"] != "block" {
277 vState.Y += inlineOffset
278 accum = (vState.Y - baseY)
279 lastHeight = vState.Height
280 } else if v.Style["position"] != "absolute" {
281 vState.Y += accum
282 inlineOffset += (vState.Height + (vState.Border.Width * 2) + vState.Margin.Top + vState.Margin.Bottom + vState.Padding.Top + vState.Padding.Bottom) + lastHeight
283 }
284 (*state)[v.Properties.Id] = vState
285 }
286}
287
288func deInline(n *element.Node, state *map[string]element.State) {
289 s := *state
290 // self := s[n.Properties.Id]
291 baseX := float32(-1)
292 baseY := float32(-1)
293 for _, v := range n.Children {
294 vState := s[v.Properties.Id]
295
296 if v.Style["display"] == "inline" {
297 if baseX < 0 && baseY < 0 {
298 baseX = vState.X
299 baseY = vState.Y
300 } else {
301 vState.X = baseX
302 vState.Y = baseY
303 (*state)[v.Properties.Id] = vState
304
305 }
306 } else {
307 baseX = float32(-1)
308 baseY = float32(-1)
309 }
310
311 if len(v.Children) > 0 {
312 deInline(&v, state)
313 }
314 }
315
316}
317
318func applyInline(n *element.Node, state *map[string]element.State) {
319 pl := inline.Init()
320 for i := 0; i < len(n.Children); i++ {
321 v := &n.Children[i]
322
323 if len(v.Children) > 0 {
324 applyInline(v, state)
325 }
326
327 if pl.Selector(v) {
328 pl.Handler(v, state)
329 }
330 }
331}
332
333func propagateOffsets(n *element.Node, prevx, prevy, newx, newy float32, state *map[string]element.State) {
334 s := *state
335 for _, v := range n.Children {
336 vState := s[v.Properties.Id]
337 xStore := (vState.X - prevx) + newx
338 yStore := (vState.Y - prevy) + newy
339
340 if len(v.Children) > 0 {
341 propagateOffsets(&v, vState.X, vState.Y, xStore, yStore, state)
342 }
343 vState.X = xStore
344 vState.Y = yStore
345 (*state)[v.Properties.Id] = vState
346 }
347
348}
349
350func countText(n element.Node) int {
351 count := 0
352 groups := []int{}
353 for _, v := range n.Children {
354 if v.TagName == "notaspan" {
355 count += 1
356 }
357 if v.Style["display"] == "block" {
358 groups = append(groups, count)
359 count = 0
360 }
361 if len(v.Children) > 0 {
362 count += countText(v)
363 }
364 }
365 groups = append(groups, count)
366
367 sort.Slice(groups, func(i, j int) bool {
368 return groups[i] > groups[j]
369 })
370 return groups[0]
371}
372
373func getMinWidth(n *element.Node, state *map[string]element.State) float32 {
374 s := *state
375 self := s[n.Properties.Id]
376 selfWidth := float32(0)
377
378 if len(n.Children) > 0 {
379 for _, v := range n.Children {
380 selfWidth = utils.Max(selfWidth, getNodeWidth(&v, state))
381 }
382 } else {
383 selfWidth = self.Width
384 }
385
386 selfWidth += self.Padding.Left + self.Padding.Right
387 return selfWidth
388}
389func getMaxWidth(n *element.Node, state *map[string]element.State) float32 {
390 s := *state
391 self := s[n.Properties.Id]
392 selfWidth := float32(0)
393
394 if len(n.Children) > 0 {
395 for _, v := range n.Children {
396 selfWidth += getNodeWidth(&v, state)
397 }
398 } else {
399 selfWidth = self.Width
400 }
401
402 selfWidth += self.Padding.Left + self.Padding.Right
403 return selfWidth
404}
405
406func getNodeWidth(n *element.Node, state *map[string]element.State) float32 {
407 s := *state
408 self := s[n.Properties.Id]
409 w := float32(0)
410 w += self.Padding.Left
411 w += self.Padding.Right
412
413 w += self.Margin.Left
414 w += self.Margin.Right
415
416 w += self.Width
417
418 w += self.Border.Width * 2
419
420 for _, v := range n.Children {
421 w = utils.Max(w, getNodeWidth(&v, state))
422 }
423
424 return w
425}
426
427func getInnerSize(n *element.Node, state *map[string]element.State) (float32, float32) {
428 s := *state
429 self := s[n.Properties.Id]
430
431 minx := float32(10e10)
432 maxw := float32(0)
433 miny := float32(10e10)
434 maxh := float32(0)
435 for _, v := range n.Children {
436 vState := s[v.Properties.Id]
437 minx = utils.Min(vState.X, minx)
438 miny = utils.Min(vState.Y, miny)
439 hOffset := (vState.Border.Width * 2) + vState.Margin.Top + vState.Margin.Bottom
440 wOffset := (vState.Border.Width * 2) + vState.Margin.Left + vState.Margin.Right
441 maxw = utils.Max(vState.X+vState.Width+wOffset, maxw)
442 maxh = utils.Max(vState.Y+vState.Height+hOffset, maxh)
443 }
444 w := maxw - minx
445 h := maxh - miny
446
447 // !ISSUE: this is a hack to get things moving adding 13 is random
448 w += self.Padding.Left + self.Padding.Right + 13
449 h += self.Padding.Top + self.Padding.Bottom
450 if n.Style["width"] != "" {
451 w = self.Width
452 }
453 if n.Style["height"] != "" {
454 h = self.Height
455 }
456 return w, h
457}
458
459func add2d(arr [][]float32, index int) float32 {
460 var sum float32
461 if len(arr) == 0 {
462 return sum
463 }
464
465 for i := 0; i < len(arr); i++ {
466 if len(arr[i]) <= index {
467 return sum
468 }
469 sum += arr[i][index]
470 }
471
472 return sum
473}
474
475func colReverse(n *element.Node, state *map[string]element.State) {
476 s := *state
477 tempNodes := []element.Node{}
478 tempStates := []element.State{}
479 for i := len(n.Children) - 1; i >= 0; i-- {
480 tempNodes = append(tempNodes, n.Children[i])
481 tempStates = append(tempStates, s[n.Children[i].Properties.Id])
482 }
483
484 for i := 0; i < len(tempStates); i++ {
485 vState := s[n.Children[i].Properties.Id]
486 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X, tempStates[i].Y, state)
487 vState.Y = tempStates[i].Y
488 (*state)[n.Children[i].Properties.Id] = vState
489 }
490
491 n.Children = tempNodes
492}
493
494func rowReverse(rows [][]int, n *element.Node, state *map[string]element.State) {
495 s := *state
496 for _, row := range rows {
497 tempNodes := []element.Node{}
498 tempStates := []element.State{}
499
500 for i := row[1] - 1; i >= row[0]; i-- {
501 tempNodes = append(tempNodes, n.Children[i])
502 tempStates = append(tempStates, s[n.Children[i].Properties.Id])
503 }
504
505 for i := 0; i < len(tempStates); i++ {
506 e := row[0] + i
507 vState := s[n.Children[e].Properties.Id]
508 propagateOffsets(&n.Children[e], vState.X, vState.Y, tempStates[i].X, tempStates[i].Y, state)
509 vState.X = tempStates[i].X
510 (*state)[n.Children[e].Properties.Id] = vState
511 }
512 for i := 0; i < len(tempStates); i++ {
513 e := row[0] + i
514 n.Children[e] = tempNodes[i]
515 }
516
517 for i := row[1] - 1; i >= row[0]; i-- {
518 vState := s[n.Children[i].Properties.Id]
519 var xChng float32
520 if i < row[1]-1 {
521 sib := s[n.Children[i+1].Properties.Id]
522 xChng = sib.X - (sib.Border.Width + sib.Margin.Left + vState.Margin.Right + vState.Border.Width + vState.Width)
523 } else {
524 parent := s[n.Properties.Id]
525 xChng = ((((parent.X + parent.Width) - parent.Padding.Right) - vState.Width) - vState.Margin.Right) - (vState.Border.Width)
526
527 }
528 propagateOffsets(&n.Children[i], vState.X, vState.Y, xChng, vState.Y, state)
529 vState.X = xChng
530 (*state)[n.Children[i].Properties.Id] = vState
531 }
532 }
533}
534
535func justifyRow(rows [][]int, n *element.Node, state *map[string]element.State, justify string, reversed bool) {
536 s := *state
537 for _, row := range rows {
538
539 if (justify == "flex-end" || justify == "end" || justify == "right") && !reversed {
540 for i := row[1] - 1; i >= row[0]; i-- {
541 vState := s[n.Children[i].Properties.Id]
542 var xChng float32
543 if i < row[1]-1 {
544 sib := s[n.Children[i+1].Properties.Id]
545 xChng = sib.X - (sib.Border.Width + sib.Margin.Left + vState.Margin.Right + vState.Border.Width + vState.Width)
546 } else {
547 parent := s[n.Properties.Id]
548 xChng = ((((parent.X + parent.Width) - parent.Padding.Right) - vState.Width) - vState.Margin.Right) - (vState.Border.Width)
549
550 }
551 propagateOffsets(&n.Children[i], vState.X, vState.Y, xChng, vState.Y, state)
552 vState.X = xChng
553 (*state)[n.Children[i].Properties.Id] = vState
554 }
555 } else if (justify == "flex-end" || justify == "start" || justify == "left" || justify == "normal") && reversed {
556 for i := row[0]; i < row[1]; i++ {
557 vState := s[n.Children[i].Properties.Id]
558 var xChng float32
559 if i > row[0] {
560 sib := s[n.Children[i-1].Properties.Id]
561 xChng = sib.X + sib.Width + (sib.Border.Width * 2) + sib.Margin.Right + vState.Margin.Left + vState.Border.Width
562 } else {
563 parent := s[n.Properties.Id]
564 xChng = parent.X + parent.Padding.Right + vState.Margin.Left + vState.Border.Width + parent.Border.Width
565
566 }
567 propagateOffsets(&n.Children[i], vState.X, vState.Y, xChng, vState.Y, state)
568 vState.X = xChng
569 (*state)[n.Children[i].Properties.Id] = vState
570 }
571 } else if justify == "center" {
572 // get width of row then center (by getting last x + w + mr + b)
573 f := s[n.Children[row[0]].Properties.Id]
574 l := s[n.Children[row[1]-1].Properties.Id]
575 parent := s[n.Properties.Id]
576 po := parent.X + parent.Border.Width
577 offset := (parent.Width - ((f.X - po) + (l.X - po) + l.Width + f.Border.Width + l.Border.Width)) / 2
578
579 for i := row[0]; i < row[1]; i++ {
580 vState := s[n.Children[i].Properties.Id]
581
582 if !reversed {
583 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X+offset, vState.Y, state)
584 vState.X += offset
585 } else {
586 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X-offset, vState.Y, state)
587 vState.X -= offset
588 }
589 (*state)[n.Children[i].Properties.Id] = vState
590 }
591
592 } else if justify == "space-between" {
593 // get width of row then center (by getting last x + w + mr + b)
594 f := s[n.Children[row[0]].Properties.Id]
595 l := s[n.Children[row[1]-1].Properties.Id]
596 parent := s[n.Properties.Id]
597 po := parent.Border.Width + parent.Width
598 po -= parent.Padding.Left + parent.Padding.Right
599
600 // make po repersent the total space between elements
601 for i := row[0]; i < row[1]; i++ {
602 vState := s[n.Children[i].Properties.Id]
603 po -= vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
604 }
605
606 po /= float32(((row[1]) - row[0]) - 1)
607
608 if (row[1]-1)-row[0] > 0 {
609 for i := row[0]; i < row[1]; i++ {
610 vState := s[n.Children[i].Properties.Id]
611 var offset float32
612 if i == row[0] {
613 offset = parent.X + parent.Padding.Left + f.Margin.Left + f.Border.Width
614 } else if i == row[1]-1 {
615 offset = (parent.X + parent.Width) - (l.Margin.Right + l.Border.Width + l.Width + parent.Padding.Right)
616 } else {
617 if !reversed {
618 offset = vState.X + (po * float32(i-row[0]))
619 } else {
620 offset = vState.X - (po * float32(((row[1]-1)-row[0])-(i-row[0])))
621 }
622
623 }
624
625 propagateOffsets(&n.Children[i], vState.X, vState.Y, offset, vState.Y, state)
626 vState.X = offset
627 (*state)[n.Children[i].Properties.Id] = vState
628 }
629 } else {
630 // if there is one element move left
631 vState := s[n.Children[(row[1]-1)-row[0]].Properties.Id]
632 var offset float32
633
634 if !reversed {
635 offset = parent.X + parent.Padding.Left + f.Margin.Left + f.Border.Width
636 propagateOffsets(&n.Children[(row[1]-1)-row[0]], vState.X, vState.Y, offset, vState.Y, state)
637 vState.X = offset
638
639 (*state)[n.Children[(row[1]-1)-row[0]].Properties.Id] = vState
640 }
641
642 }
643
644 } else if justify == "space-evenly" {
645 // get width of row then center (by getting last x + w + mr + b)
646 parent := s[n.Properties.Id]
647 po := parent.Border.Width + parent.Width
648 po -= parent.Padding.Left + parent.Padding.Right
649
650 // make po repersent the total space between elements
651 for i := row[0]; i < row[1]; i++ {
652 vState := s[n.Children[i].Properties.Id]
653 po -= vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
654 }
655
656 po /= float32(((row[1]) - row[0]) + 1)
657
658 // get width of row then center (by getting last x + w + mr + b)
659
660 for i := row[0]; i < row[1]; i++ {
661 vState := s[n.Children[i].Properties.Id]
662
663 if !reversed {
664 offset := po * (float32(i-row[0]) + 1)
665 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X+offset, vState.Y, state)
666 vState.X += offset
667 } else {
668 offset := po * float32(((row[1]-1)-row[0])-((i-row[0])-1))
669
670 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X-offset, vState.Y, state)
671 vState.X -= offset
672 }
673 (*state)[n.Children[i].Properties.Id] = vState
674 }
675
676 } else if justify == "space-around" {
677 // get width of row then center (by getting last x + w + mr + b)
678 parent := s[n.Properties.Id]
679 po := parent.Border.Width + parent.Width
680 po -= parent.Padding.Left + parent.Padding.Right
681
682 // make po repersent the total space between elements
683 for i := row[0]; i < row[1]; i++ {
684 vState := s[n.Children[i].Properties.Id]
685 po -= vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
686 }
687
688 po /= float32(((row[1]) - row[0]))
689
690 // get width of row then center (by getting last x + w + mr + b)
691
692 for i := row[0]; i < row[1]; i++ {
693 vState := s[n.Children[i].Properties.Id]
694
695 if !reversed {
696 m := (float32(i-row[0]) + 1)
697 if i-row[0] == 0 {
698 m = 0.5
699 } else {
700 m -= 0.5
701 }
702 offset := po * m
703 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X+offset, vState.Y, state)
704 vState.X += offset
705 } else {
706 m := float32(((row[1] - 1) - row[0]) - ((i - row[0]) - 1))
707 m -= 0.5
708 offset := po * m
709
710 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X-offset, vState.Y, state)
711 vState.X -= offset
712 }
713 (*state)[n.Children[i].Properties.Id] = vState
714 }
715
716 }
717
718 }
719}
720func alignItemsRow(rows [][]int, n *element.Node, state *map[string]element.State, justify string, reversed bool) {
721
722}